-
Notifications
You must be signed in to change notification settings - Fork 69
Sequence of .then() calls with Promise.all #25
Comments
You can never synchronously call a promise handler; they are always called via TriggerPromiseReactions which enqueues tasks to call them. |
Just to make that clear, you're saying that when And that this sequence is required by the spec. (edited to add: I'm not trying to be pedantic, I just want to make sure I have the observables right, because my reasoning could be in error about the other things -- including whether a synchronous call is what causes this.) |
Yes. You can also check against the reference implementation, if that would help. |
Thanks. I can see that the sequencing behavior of v8 faithfully reproduces the sequencing behavior of the reference implementation. Changed the tests to reflect expected sequencing behavior. |
So the conclusion is that NPO has a bug in Promise. all, correct? |
Yes, and also Promise.race. I think it's actually the scheduler, but I haven't gone into the code yet. |
I'll jump on it. |
should be fixed now I think. I was being too aggressive with perf optimizations ☺ |
That causes the Promise.all tests to pass, but not the Promise.race tests. I glanced at the NPO code and didn't see the problem, so my natural inclination is to blame the tests. Will do some more checking. |
The test I used for |
Hm.. what version of node are you running? I just noticed that for NPO the This is the command I am using to run the race tests: And the result:
|
NPO doesn't run in 0.11.13, because it's a polyfill, and in 0.11, I am running 10.29, and my |
Oh. Hahahaha. Of course. I will create a small breaking test. |
Confirmed that node version doesn't matter by modifying the test_adapter to remove global.Promise before loading NPO. I am surprised that node 0.11 has global.Promise even if I don't pass --harmony on the command line. Anyway, a failing test:
|
And the other failing test:
|
See promises-aplus/promises-tests#61 Notice that I reduced the test cases to actually have nothing to do with
[update: my bad, see message below] |
Neuralgia is a known side effect of open-source developmen I will port your simplified tests to @domenic 's reference implementation and note the results. |
OK, so both @smikes' test cases were correct, and I was misunderstanding how to think about scheduling vs. resolution/rejection. I have corrected NPO in v0.7.0-a to use the correct scheduling behavior (at least, I believe), and it passes both tests as shown above. |
Ah, schneaky. The "race" was between scheduling the execution and subsequently adding a handler to the execution chain. |
I do think we should have tests of the sequencing (like my simplified versions) and tests with |
Agreed. I can add those easily. They exercise the scheduler directly; while the |
I have cleaned up one of the tests that shows a difference in behavior between NPO and v8 -- since this is the version of v8 which is known not to pass the Promises/A+ suite, I was initially inclined to suspect that this is reacting to a problem in v8. Now I am not so sure, and I would appreciate guidance. (/cc @domenic @allenwb )
Here is the test:
My expectation is that the three functions passed to
then
will be enqueued and eventually executed in sequence: (afterOne
,afterAll
,afterTwo
). However, I am concerned that my expectations are coloring my reasoning as I read the spec.According to 25.4.4.1 step 9.o
all
invokesthen
on each of the promises in its argument. (Technically there's an intermediate step wherePromise.resolve
is called on each argument, but since p1 and p2 were constructed by the same Promise constructor as Promise.all,Promise.resolve
just returns them without constructing new promises.)According to 25.4.4.3 step 12, when
then
is called on a promise in the state"fulfilled"
, a task to call itsonFulfilled
handlers is enqueued immediately.Both p1 and p2 are constructed by
Promise.resolve()
so they begin in the"fulfilled"
state.I would expect the state of the
"PromiseTasks"
queue to evolve as follows:After control flow leaves the test function, I would expect tasks to be consumed from the
"PromiseTasks"
queue in FIFO order. The question is, what happens when resolveElement2 finishes executing?According to 25.4.4.1.1 step 10, when
remainingElementsCount.[[value]]
has dropped to zero, the parent Promise created byPromise.all
should have itspromiseCapability.[[Resolve]]
function called. This will eventually trigger a call to the only function in itsonFulfilled
slot, namelyafterAll
.The question is, can
resolveElement2
synchronously callafterAll
(since it is running in the PromisesTask handler, not from user code) or must it enqueue the call toafterAll
? This is detectable by user code as above, where the numbers pushed tosequencer
distinguish between the call order of (afterOne
,afterAll
,afterTwo
) vs. (afterOne
,afterTwo
,afterAll
)I started writing this thinking that my test was correct, and afterAll should be called before afterTwo. Midway through writing this, I flip-flopped and started to think that afterTwo should be called before afterAll. Now I am merely confused.
Comments very much appreciated.
The text was updated successfully, but these errors were encountered: