GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — util: add util.promisify() by addaleax · Pull Request #12442 · nodejs/node Conversation
|
|
Also, @benjamingr has a good point about the naming, quote:
We can bikeshed this further here, but just to get an idea how people feel, could people 👍 this comment for “ |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Maybe we should split the timers bits into a second PR? They don't map well to promises since we don't have cancelables. Maybe we could plug the timers props onto the promise itself? Not sure. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I.e. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I (also) would like to see the timers impl in a followup PR.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This truthy test of orig is not needed since it is guaranteed to be a function at this point
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
@williamkapke thanks for catching, done
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
A big +1 on this. I think this could be a great first step for helping users deal with |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Going to expand on my point a bit: I think this is reasonable to do for simple callback APIs but I think we should spend more* time to think about how to wrap more complex APIs such as timers, child processes, etc well.
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
If that’s what people here prefer, sure. I just thought it made sense because they’re about the only API that’s a bit icky because they don’t conform to Node’s standard callback pattern, so whenever we introduce something like
Makes sense, I’ve updated the PR with |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
I definitely agree - we need a proper promised core at some point - but #5020 and similar attempts have stagnated and quoting myself in the original issue:
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I think @Fishrock123’s comment was about the timers stuff, not the general API here? I feel inclined to disagree that this needs an EP, though… |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Correct. I only mean for how to wrap any APIs that are more complex than single events or callbacks and which do not wrap as well. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
function isn't a legal identifier in JS, and for consistency we probably want to keep it legal.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
* {symbol}
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Lower case "custom"
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I think the uppercase variant is okay here, it’s referring to a literal section title, and it matches what we do for the other two section references in this file. If you feel strongly about it I’ll change it.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
An async function example would be cool as well.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
You probably want to clarify setImmediate() or its promisified equivalent.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
"Using the util.promisify.custom symbol"?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
What about the prototype of orig?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
@TimothyGu yeah … that thought occurred to me, but I basically didn’t see how setting it would make sense here. If you prefer, I’ll add
Object.setPrototypeOf(fn, Object.getPrototypeOf(orig));
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Yeah I don't see how it would matter that much in real world, but having that there does seem a bit more natural to me. Up to you if you want to set it or not.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I don't think it would matter that much; it just occurred to me having the prototype being equivalent might be more natural. Up to you.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
@TimothyGu I’ve included it for now, let’s see what people think.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
ad34f91 to
fe27d6a
Compare
|
@TimothyGu Addressed most of your comments, PTAL |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This seems like a bad idea for many reasons. I think it's fine if you can't cancel promisified timeout until we have some generic cancellation mechanism.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This seems like a bad idea for many reasons.
Would you care to provide some? Otherwise this is just not helpful.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
First of all, attaching properties to native objects seems like a bad idea from performance perspective. Secondly, this implements ad hoc cancellation mechanism, while there is no spec on standards track or even community consensus. This approach suffers from the fact that it's not propagated and there it doesn't play nice with async-await.
I think it's fine to make promisified versions not cancellable. If you use them you are most likely not going to cancel anyway.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
First of all, attaching properties to native objects seems like a bad idea from performance perspective.
That’s true, but it’s opt-in, right?
This approach suffers from the fact that it's not propagated
What do you mean by propagated?
But yeah, I would be okay with dropping clear* support for these, at least for the time being.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by propagated?
Something like this:
const promise = setTimeout(1000) .then(() => ...); clearTimeout(promise); // This won't work, you need original promise
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I have to agree with @vkurchatkin on this. Until there is a standard way of canceling promises, we likely shouldn't do this.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
@vkurchatkin @jasnell Okay, I’ve dropped clearTimeout/clearImmediate support. It’s a niche feature anyway and it should be easy to readjust later if we want.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Add `util.promisify(function)` for creating promisified functions. Includes documentation and tests. Fixes: nodejs/CTC#12 PR-URL: nodejs#12442 Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Myles Borins <[email protected]> Reviewed-By: Evan Lucas <[email protected]> Reviewed-By: William Kapke <[email protected]> Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Teddy Katz <[email protected]>
Take tests from Bluebird's promisify's tests and adapted them to the format in use here. Add tests making sure things work with async functions. Add basic usability tests. PR-URL: nodejs#12442 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Myles Borins <[email protected]> Reviewed-By: Evan Lucas <[email protected]> Reviewed-By: William Kapke <[email protected]> Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Teddy Katz <[email protected]>
PR-URL: nodejs#12442 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Myles Borins <[email protected]> Reviewed-By: Evan Lucas <[email protected]> Reviewed-By: William Kapke <[email protected]> Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Teddy Katz <[email protected]>
Add support for `util.promisify(setTimeout)` and `util.promisify(setImmediate)` as a proof-of-concept implementation. `clearTimeout()` and `clearImmediate()` do not work on those Promises. Includes documentation and tests. PR-URL: nodejs#12442 Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Myles Borins <[email protected]> Reviewed-By: Evan Lucas <[email protected]> Reviewed-By: William Kapke <[email protected]> Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Teddy Katz <[email protected]>
PR-URL: nodejs#12442 Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Myles Borins <[email protected]> Reviewed-By: Evan Lucas <[email protected]> Reviewed-By: William Kapke <[email protected]> Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Teddy Katz <[email protected]>
Author: Benjamin Gruenbaum <[email protected]> Author: Anna Henningsen <[email protected]> PR-URL: nodejs#12442 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Myles Borins <[email protected]> Reviewed-By: Evan Lucas <[email protected]> Reviewed-By: William Kapke <[email protected]> Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Teddy Katz <[email protected]>
PR-URL: nodejs#12442 Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Myles Borins <[email protected]> Reviewed-By: Evan Lucas <[email protected]> Reviewed-By: William Kapke <[email protected]> Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Teddy Katz <[email protected]>
|
Landed in f72376d...faf6654 Thanks everyone for the reviews and support! |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@addaleax going to give a small talk about this journey on Thursday, starting in nodejs/node-v0.x-archive#7642 Thanks a ton for taking up the glove and working together on the conceptual side, while undertaking the vast majority of the technical burden. Props for the execution and delivery, and thank you :) |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
@MadaraUchiha You were right! This PR is @benjamingr's birthday present 😉 |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I just published https://www.npmjs.com/package/util.promisify (with code largely copy/pasted from this PR, with permission from @addaleax) for those interested. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| promiseResolve(promise, values[0]); | ||
| } | ||
| }); | ||
| } catch (err) { |
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This catch may accidentally catch errors thrown from promiseReject if orig.call happened to be synchronous. E.g. the value was cached in memory and didn't need a remote lookup. promiseReject on line 264 will be a no-op in that case.
Ideally if the execution hit line 253 and then 264, it should re-throw the error instead of no-oping. At least it could log a warning.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The existing way is definitely better; a function that returns a promise should never throw, only return a rejected promise.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
@syrnick can you please open an issue about it?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
(also, since a promise can only represent one value; new Promise((resolve, reject) => { reject(1); reject(2); }) is supposed to have the second rejection be a noop, and does not throw the 2, so it'd be important for util.promisify to maintain parity)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| }; | ||
|
|
||
| const kCustomPromisifiedSymbol = Symbol('util.promisify.custom'); | ||
| const kCustomPromisifyArgsSymbol = Symbol('customPromisifyArgs'); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
What's the motivation for keeping this private? It's obviously useful in a number of places.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
but that's still on internal/util, not accessible to user code. The other one(kCustomPromisifiedSymbol) is available as util.promisify.custom.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
Support awaiting values throughutil.awaitable
There was an error while loading. Please reload this page.