You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When I pass an async typescript function into a command's action() method, the async function is called correctly, but is not run to the end, as the program does not wait when awaiting another function. It simply ends execution. I've seen a similar question here, but it was not answered, as far as I understand.
Consider this example:
#!usr/bin/env node
import * as commander from 'commander';
import axios from 'axios';
async function run() : Promise<void> {
var response = await axios.get("https://dummyimage.com/600x400/000/fff", { "responseType": "arraybuffer" });
if (response.status === 200) {
console.log(`Downloaded file: ${response.data.byteLength} bytes.`)
}
else {
console.error('Error downloading');
}
}
async function main() {
commander
.command("do", "Test")
.action(run)
.parse(process.argv);
// await run();
console.log('finished');
}
try {
main();
}
catch (error) {
console.error(error);
}
If you run the example above, you can see that finished is called but the download is never finished. Calling await run() without the help of commander works, though.
So, is it possible to use commander with typescript's async functions?
Reacted by Nick Addison, Dominic Foti Jr., JP, Mike, Laurent Grima, Ethan Lin, Towry Wang, Victor Shelepen, Neil Olson, Roman Petrov and 6 more
I investigated yargs as an option but frankly I think commander.js offer the best design. However that doesn't mean that commander.js does not have an issue in this regard.
I am very experienced but quite new to JavaScript. It leaves a lot to be desired when compared to most other languages.
With that said, I find myself in a position where I have to build an enterprise-quality command-line app that must run under node.js. The app will also offer a REST API interface to some functions. Obviously neither it, nor the CLI can be allowed to block the message/UI thread.
commander.js seems to be a little schizophrenic in this regard. Is it a parser or a command dispatcher? It's a really nice parser. Dispatcher? No so much. Command actions definitely do not block the thread however there is no reasonable way to return anything from the handling function, including a Promise.
I have evaluated a few options but none of them will allow me to sleep well at night after this thing goes live. So I have a suggested change:
Offer a version of command.parse that doesn't actually dispatch an action but rather, returns the objects that would have been supplied when calling the action. This would allow the consuming app to dispatch it's own method using either Promise or TypeScript async/await.
Offer awaitable and sync versions of command.parse that returns whatever value is returned by the action function.
I would suggest option 1 as it provides a clean separation between parsing and functional execution.
I thought about forking this project and making the changes myself but I have to say my learning curve would make it too costly and that same learning curve would probably produce less than optimal source code. If you were not able to accept our pull request then we would be in the situation where we would have to merge your changes into our version, forever.
This is just food for thought. For our project we may write a parser from the ground up using TypeScript and the semantics of commander.js in the spec. Another possibility will be to just write a parser specific to the apps requirements.
By the way, if there is something I have wrong, please feel free to constructively enlighten me.
Reacted by Oliver Uvman, Ethan Lin, Robbie Cronin and KittenWithHerbs
I think this behavior is correct. Exactly, promise-like return is not provided through commander api though.
Why not wrap commander with promise api manually? By the way, I also don't think that it is needed to do jobs simultaneously around commander initiation. Generally, commander initiation should be down at the top level of a problem or a system at the entry file, which, I think, is a best practice.
I had the same issue, turned out it was a version problem.
Upgrading from version 2.15.0 to 2.18.0 fixed the issue.
I have 2.19.0 The async actions are not managed well.
I looked in the code. I see it is not prepared for async chaining at all. EventEmmiter is used to execute a command. Is a strategy pattern enough? Do contributors have plans to adjust it to be chainable?
Oddly, some async tasks run to completion. Others do not. In particular, waiting on a promise that never resolves exits with status 0, which is a big no-no. version 3.0.2
So, we would add a .parseAsync() which you should use instead of .parse() if you have any async action handlers, and it returns whatever the action handler returns. Up to calling code to call the correct parse and do the right thing with what it returns.
Might that work for people wanting to use async?
Reacted by Tim Knipe, Cédric Belin, Fabien M and Mathew Goldsborough
Activity
sr258 commentedon May 30, 2018
bigtfromaz commentedon Aug 12, 2018
I investigated yargs as an option but frankly I think commander.js offer the best design. However that doesn't mean that commander.js does not have an issue in this regard.
I am very experienced but quite new to JavaScript. It leaves a lot to be desired when compared to most other languages.
With that said, I find myself in a position where I have to build an enterprise-quality command-line app that must run under node.js. The app will also offer a REST API interface to some functions. Obviously neither it, nor the CLI can be allowed to block the message/UI thread.
commander.js seems to be a little schizophrenic in this regard. Is it a parser or a command dispatcher? It's a really nice parser. Dispatcher? No so much. Command actions definitely do not block the thread however there is no reasonable way to return anything from the handling function, including a Promise.
I have evaluated a few options but none of them will allow me to sleep well at night after this thing goes live. So I have a suggested change:
Offer a version of command.parse that doesn't actually dispatch an action but rather, returns the objects that would have been supplied when calling the action. This would allow the consuming app to dispatch it's own method using either Promise or TypeScript async/await.
Offer awaitable and sync versions of command.parse that returns whatever value is returned by the action function.
I would suggest option 1 as it provides a clean separation between parsing and functional execution.
I thought about forking this project and making the changes myself but I have to say my learning curve would make it too costly and that same learning curve would probably produce less than optimal source code. If you were not able to accept our pull request then we would be in the situation where we would have to merge your changes into our version, forever.
This is just food for thought. For our project we may write a parser from the ground up using TypeScript and the semantics of commander.js in the spec. Another possibility will be to just write a parser specific to the apps requirements.
By the way, if there is something I have wrong, please feel free to constructively enlighten me.
chigix commentedon Oct 9, 2018
I think this behavior is correct. Exactly, promise-like return is not provided through commander api though.
Why not wrap commander with promise api manually? By the way, I also don't think that it is needed to do jobs simultaneously around commander initiation. Generally, commander initiation should be down at the top level of a problem or a system at the entry file, which, I think, is a best practice.
alexx666 commentedon Nov 2, 2018
I had the same issue, turned out it was a version problem.
Upgrading from version 2.15.0 to 2.18.0 fixed the issue.
chrisgo commentedon Feb 27, 2019
Can this pattern be used?
https://stackoverflow.com/questions/48376479/executing-multiple-sequelize-js-model-query-methods-with-promises-node?answertab=oldest#tab-top
sr258 commentedon Feb 28, 2019
@chrisgo I'm sure it can, but the point of my question was if it is possible to use async await.
victor-shelepen commentedon Mar 8, 2019
victor-shelepen commentedon Mar 8, 2019
I looked in the code. I see it is not prepared for async chaining at all. EventEmmiter is used to execute a command. Is a strategy pattern enough? Do contributors have plans to adjust it to be chainable?
shadowspawn commentedon Oct 3, 2019
I came across this issue on Yargs to improve their async support. Some interesting detail on possible code patterns: yargs/yargs#1420
jdmarshall commentedon Oct 24, 2019
Same problem.
Oddly, some async tasks run to completion. Others do not. In particular, waiting on a promise that never resolves exits with status 0, which is a big no-no. version 3.0.2
jdmarshall commentedon Oct 24, 2019
Cheese it:
shadowspawn commentedon Nov 23, 2019
I am wondering about option 2 from #806 (comment)
So, we would add a
.parseAsync()which you should use instead of.parse()if you have any async action handlers, and it returns whatever the action handler returns. Up to calling code to call the correct parse and do the right thing with what it returns.Might that work for people wanting to use async?
shadowspawn commentedon Dec 11, 2019
I have added a draft Pull Request to add
.parseAsync(). Feedback from interested parties welcome.shadowspawn commentedon Jan 6, 2020
Added
.parseAsync()in Commander v4.1.See https://github.com/tj/commander.js#action-handler-subcommands
fix: action can return an async function
fix(ts): action can be an async function
fix(ts): action can be an async function
fix(ts): action can be an async function
fix(ts): action can be an async function
fix(ts): action can be an async function (#1157)
fix(ts): action can be an async function (#1157)
fix(ts): action can be an async function (#1157)