-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
fix(std): Converting Deferred interface to class #8601
Conversation
As discussed in std-wg. Could you remove the TODO comment, and update the tests? |
Yeah, sure thing. I just wanted to create a draft PR to hear everyone's opinions. |
Also there is a problem with current implementation.
would actually instantiate a deferred<{}>. According to microsoft/TypeScript#39817, unless promise type is void, resolve should pass an argument of type T. In current implementation due to some bug, this is bypassed. The fault is fixed in this PR. |
CC @ry @lucacasonato |
@@ -338,7 +338,7 @@ unitTest( | |||
unitTest( | |||
{ perms: { net: true } }, | |||
async function netTcpListenIteratorBreakClosesResource(): Promise<void> { | |||
const promise = deferred(); | |||
const promise = new Deferred<void>(); |
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.
Is it necessary to put the <void>
in here?
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.
Yes, or else the default type is {}
and mandates a parameter of type {}
when resolve is called. This fix was introduced in a later version of typescript though (microsoft/TypeScript#39817).
Edit: This was introduced in the latest version of typescript released last month.
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.
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.
Writing new Deferred<void>
is fairly obnoxious. Much longer than deferred
. We should reconsider this change.
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.
@ry Additionally, extending Promise
has some annoying behaviours which forces it to accept an executor function, which deferred()
did not. It makes .then()
calls also return Deferred
instances which makes no sense.
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 actually pretty happy with current implementation, I think we should only change Object.assign()
bit to directly assign resolve
and reject
to promise
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.
@ry @nayeemrmn, I think we'll need to explicitly write deferred<void>
too. Not mentioning correct type in https://github.com/denoland/deno/blob/master/std/async/deferred.ts#L8 allows current behaviour but has the downside of not following latest promise structure. But that can be fixed by adding right type to current implementation too.
I don't mind the current implementation either but addition to @bartlomieju comment, I think we should consider following typescript's promise structure
Looks like this is an anti-pattern . Also, refer https://github.com/denoland/deno/issues/8405#issuecomment-739420300. |
@iammack it can be useful, in fact in your anti-pattern link it says:
Sometimes you have to. |
@hayd, it would be helpful if you could provide an example where using
|
@iammack An example where this allows for an intuitive algorithm is a connection pool, where the async method returns a Connection object, which is then released (put back into the connection pool) once it's been used. If there isn't a Connection object available it waits until one has been released. (e.g. here.) |
Apologies for the late reply, been stuck in some academics related work. I think @benjamingr should weigh in here, he has better knowledge of this :) |
My opinion (was) and still is that going against the standard by not using the provided construction method (the promise constructor) that is designed to be throw-safe and instead using the old (and removed from the spec) Deferred is a mistake. I would just refactor things to the promise constructor or better - avoid it in most places altogether. |
Of course - Deno is entirely at liberty to introduce its own abstractions rather than use JavaScript's for promise construction. Just in case the above implies differently. It's just kind of weird to pick the variant TC39 ruled out for being unsafe and foot-gunny. |
@benjamingr could you elaborate on this one? What would you suggest instead? |
Sure: var d = Deferred();
someAsyncOp(() => {
d.resolve();
});
return d.promise; Becomes:
Or better - you can call |
@benjamingr What about, say, I'm also against overriding tc39's intentional design without good domain-specific reasons. But I think a lot of the call sites in Deno's codebase would just be replaced by an inline version of |
When you explicitly need indirection like that - you have no choice but to pick a way to "escape" the promise constructor. I would not use a Probably just inline it:
Note this is still not great but I'm not sure what to do - I will ask Domenic an some people who were in TC39 when the decision happened what they think. P.S. why does that code |
What about something like:
I don't mind writing some code if the gist of it is unclear. |
Deno standard library was moved to denoland/deno_std, please reopen your PR there. |
Converts Deferred interface in std/async to class. The deferred function can be removed then.