-
Notifications
You must be signed in to change notification settings - Fork 46
[Spec] figure out how to return/propagate promises properly #18
Comments
I think I figured out what would work here. First, a minimal example of what we're doing. Assume the environment provides some abstract op, e.g. PromiseReturningSetTimeoutFromEnvironment. It returns a promise, via magic, that fulfills after the given amount of time. We want to write an abstract op that returns a promise that fulfills with 42 after one second. How would we attempt this? Here's what #15 does: UsesAwait()
This seems a bit fishy. Let's expand it out by inlining Await and PromiseReturningSetTimeoutFromEnvironment: UsesAwaitUnrolled()
The problem is pretty clear now. We only return to the caller after a suspend-resume cycle. So if we have a caller, like Caller()
What is x? Well, it is indeed a promise, as desired. BUT, to get that promise, we might have had our execution context suspended out from under us. Instead we really wanted something like this: UsesAwaitUnrolled2()
This is a tiny change: it just says that we return the promise once all the setup is done, instead of returning it after the evaluation state has resumed. But it does what we want. That's as far as I've gotten so far. The next big question is, can we figure out some modification to Await(), or some other abstraction, which works "as we want" in this case? Thinking... |
I don't think there's a difference between UsesAwaitUnrolled() and UsesAwaitUnrolled2(). |
@jmdyck so sorry about that. Apparently two-space indents are not good enough for GitHub. Fixed. One more long post coming... |
Here's something that could work. Introduce a new abstract op, AwaitWithReturn: AwaitWithReturn(promise)Algorithm steps that say
Mean the same thing as
Similarly, you can write
to mean the same thing, except that your steps don't take an argument value. Note that you never use ! and ? with AwaitWithReturn. The difference is that AwaitWithReturn(p) behaves more like the In contrast, Await() adds a handler to I am pretty sure we need both still... With this in hand, we can then write things like: UsesAwait2()
Here the "Return 42" got stuffed into the "onFulfilled callback" of AwaitWithReturn, so this works as expected. We could also do UsesAwait3()
and this works as expected: it's basically (Better names for AwaitWithReturn welcome.) |
@domenic just catching up on this here - I don't have any knowledge of the Promises spec, but would this |
That probably depends on how tc39/ecma262#1250 goes. It would definitely still cause a job enqueued for the PerformPromiseThen, but if we are able to land 1250 (and I hope we can), then there wouldn't be another job for promise resolution.
This depends on what you mean. EnqueueJob, at least in browsers, is still "synchronous"; it just gets pushed to the end of the task. A serious of enqueued jobs (as you would see, for example, in a series of modules that immediately resolve their evaluation promise by virtue of not having any |
@domenic thanks for the clarification, in that case I can look into a spec PR here if it would help. One question I had - what does error handling look like in this model? Is it just the usual abrupt completion check? |
These abstractions seem nice, but I am not sure if they are needed for top-level await. All we have to do is Promise.all the dependencies, then execute the body, and let that body execution settle the surrounding capability. I think we can do this just fine with then-based notation, as in guybedford#1 . |
The current specification text waits for things without introducing any new notation. |
I managed to get myself pretty royally confused when I was last in this area, tentatively concluding that everything was broken and we needed a lot more work. But am happy to trust you, if you feel un-confused. |
@domenic To be honest, I still don't understand what you were trying to get at there; I'm pretty confident in the mechanics of the current spec text, though (but I have some editorial changes coming based on your feedback). |
#15 has a problem; I'm not sure how easy it is to fix. Namely, it does stuff like
This doesn't really work, because all of the steps after step 3---including the return step---get sucked into the "await". So we never really return anything to the caller.
That is, as currently written, abstract ops that use Await() never return anything. Kinda? Except sometimes they do, in the current spec, in cases where it's OK for the caller to suspend itself?
This has worked OK for Await() usages so far, but here we really need a series of promise-returning abstract ops which call each other. I'm not sure what the right fix is. There might be something quick. Or we might need to do tc39/ecma262#933. Or maybe we need to mutate Await() to cause its caller to return a promise? But that seems to clash a bit with other uses, hmm.
One issue here is that we would really benefit from "await-like" technology, instead of "then-like" technology, because we're inside a loop. I can see how to solve this general class of problem by converting to "then-like" technology, but then my loop will be extraordinarily ugly (and also "then-like" technology is very verbose currently; that's tc39/ecma262#933).
The text was updated successfully, but these errors were encountered: