-
Notifications
You must be signed in to change notification settings - Fork 995
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Async command handlers #1001
Async command handlers #1001
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is looking good 👍
// addresses https://github.com/yargs/yargs/issues/510 | ||
it('fails when the promise returned by the command handler rejects', (done) => { | ||
const error = new Error() | ||
yargs('foo') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it might be worth adding a test for the happy path too (the promise resolving).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, but I can't think of anything to assert in that case... This feature does nothing when the handler result is thenable and it resolves.
I had actually fixed (?) my resolution handler in lib/command.js
from a () => {}
noop to null
so that it would not show as an untested code path in the test coverage reports.
Anyone have a suggestion of what to assert? What do we need to watch out for in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hey, made comment above with recommendation about assertion; I suppose now that the fail handler is called async, you can just call done()
here after your promise resolves?
if (handlerResult && typeof handlerResult.then === 'function') { | ||
handlerResult.then( | ||
null, | ||
(error) => yargs.getUsageInstance().fail(null, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would probably just fail with err.message
rather than null
, I think your changes in usage.js
probably wouldn't be needed then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm passing null here and making the changes in usage.js
so that the error with stack trace gets logged to the console, similar to what happens when an error is thrown in a sync command handler.
(On that note, maybe we should align how errors are handled in sync vs async command handlers, and handle sync errors as "failures" too?)
It seems important to have the stack trace logged to the console, and it seems like this is the simplest way to achieve it.
I should probably add a test to make sure this happens. I will add one if you agree that that's what we want to happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry for the slow reply -- this logic seems reasonable to me. Let's add a test for both this and the happy path. To test the happy path, I would just have a command handler that modifies a variable, and assert that it executed as expected:
let asyncHandlerCalled = false
const asyncHandler = new Promise((resolve, reject) => {
asyncHandlerCalled = true
return resolve()
})
yargs('foo')
.command('foo', 'foo command', noop, (yargs) => Promise.reject(error))
something like that; you'll need to make sure the test is async, etc.
Looks really promising! 👏 (pun intended 😉 )
This is a whole different topic, as async argument validation would turn whole As a workaround, you could throw a specific error (e.g. So getting this enhancement in without async argument validation would be awesome! |
@zcei It's promising, and also thenabling 😉 (Jokes, How long have you been a dad btw? My kids are 8 and 10, Jokes)
Ah, that explains it. Thanks. (D'uh) Yeah changing yargs to do validation asynchronously seems like it have to have some significant breaking changes, since (correct me if I'm getting this wrong) it must be either always async or always sync, or Zalgo get's released. Maybe we should open a separate issue for that? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated with comments, sorry for the slow turn around.
if (handlerResult && typeof handlerResult.then === 'function') { | ||
handlerResult.then( | ||
null, | ||
(error) => yargs.getUsageInstance().fail(null, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry for the slow reply -- this logic seems reasonable to me. Let's add a test for both this and the happy path. To test the happy path, I would just have a command handler that modifies a variable, and assert that it executed as expected:
let asyncHandlerCalled = false
const asyncHandler = new Promise((resolve, reject) => {
asyncHandlerCalled = true
return resolve()
})
yargs('foo')
.command('foo', 'foo command', noop, (yargs) => Promise.reject(error))
something like that; you'll need to make sure the test is async, etc.
// addresses https://github.com/yargs/yargs/issues/510 | ||
it('fails when the promise returned by the command handler rejects', (done) => { | ||
const error = new Error() | ||
yargs('foo') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hey, made comment above with recommendation about assertion; I suppose now that the fail handler is called async, you can just call done()
here after your promise resolves?
@zenflow think you'll have a chance to see this over the finish line? otherwise hopefully I'll have some time over the holiday. |
@bcoe Sorry for all the delay - I'm finally free from work for a while now, after a few mad weeks of finishing up, documenting, handing off, etc. I'll definitely have time to finish this off over the next |
# Conflicts: # lib/command.js
One (un-documented) side-effect of this change is that TL;DR: If you use async command handlers with |
@evocateur What does a "chained |
The yargs API method ".fail()". The parse() method only accepts a callback, it does not return a promise.
… On Aug 1, 2018, at 21:01, Tim Kye ***@***.***> wrote:
@evocateur What does a "chained fail callback look like"? Is that chained onto parse or the callback?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
the typescript types should also be updated |
yes i've just run into the same issue |
what' progress of this issue? |
@tianyingchun About updating types? |
Addresses issue #510
"Allow command handler to accept a promise""Support async (i.e. promise-returning) command handlers"This PR handles "update [...] invocation of a command handler to understand a promise". Nothing is done if the promise resolves, but if it rejects, it is handled as "failure" with an
err
but nomsg
.@bcoe Not sure what needs to be done about "update the argument validation"... It seems like there's no change needed; arguments to command handlers, and validation of said arguments, aren't affected by this feature. This feature deals only with what is returned by the command handler (a promise, potentially). Am I missing something?
This PR also makes a slight change to the default failure handler, so that if there's no
msg
but there is anerr
,err
will be logged instead, which will display the full stack trace for debugging.Thank you for your work on this project, and taking the time to review this change :)