Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Fix ZoneAwarePromise.all to resolve at the correct time #1150

Merged
merged 1 commit into from
Oct 26, 2018
Merged

Fix ZoneAwarePromise.all to resolve at the correct time #1150

merged 1 commit into from
Oct 26, 2018

Conversation

trevorade
Copy link
Contributor

@trevorade trevorade commented Oct 23, 2018

For ZoneAwarePromise.all, as implemented, the count variable serves three purposes:

  1. Count the number of values passed-in
  2. Specify the index of a resolved value in resolvedValues
  3. Track when all the promises have been resolved.

In the event that value.then is immediately called, count will be decremented at the incorrect time resulting in a potentially negative value or reaching 0 when not all values have actually been resolved.

The fix is to be more meticulous about using the correct indices at the correct time and to not overload the count and number of resolved promises. This updated version is more explicit about the purpose of the unresolvedCount and valueIndex variables. unresolvedCount needs to start at 2 to prevent resolve from being called too soon in the event that value.then calls the callback immediately. The scoping of the index for use in resolvedValues is made constant to guarantee the correct index is used.

This buggy behavior specifically manifested as an issue in the Monaco editor but most likely occurs elsewhere as well in cases where promises may immediately call into .then.

Related issue:
microsoft/monaco-editor#790 (comment)

This fixes #1077

For ZoneAwarePromise.all, as [implemented](https://github.com/angular/zone.js/blob/7201d44451f6c67eac2b86eca3cbfafde14035d6/lib/common/promise.ts), the `count` variable serves three purposes:

1.  Count the number of values passed-in
2.  Specify the index of a resolved value in `resolvedValues`
3.  Track when all the promises have been resolved.

In the event that `value.then` is immediately called, `count` will be decremented at the incorrect time resulting in a potentially negative value or reaching 0 when not all values have actually been resolved. 

The fix is to be more meticulous about using the correct indices at the correct time and to not overload the count and number of resolved promises.  This updated version is more explicit about the purpose of the `unresolvedCount` and `valueIndex` variables.  `unresolvedCount` needs to start at 2 to prevent `resolve` from being called too soon in the event that `value.then` calls the callback immediately. The scoping of the index for use in `resolvedValues` is made constant to guarantee the correct index is used.

This buggy behavior specifically manifested as an issue in the Monaco editor but most likely occurs elsewhere as well in cases where promises may immediately call into `.then`.

Related issue:
microsoft/monaco-editor#790 (comment)
@evmar
Copy link
Contributor

evmar commented Oct 23, 2018

cc @rkirov

@evmar
Copy link
Contributor

evmar commented Oct 23, 2018

Maybe mention this fixes #1077

@trevorade
Copy link
Contributor Author

The continuous-integration/travis-ci/pr failure appears to be a disconnect. I didn't see any failing tests. I don't know how to re-run those tests.

@JiaLiPassion
Copy link
Collaborator

JiaLiPassion commented Oct 24, 2018

I remembered some other guy get the same issue before, I think this is the bug of Monaco editor.
The callback should not be immediately called. It will obey the promise spec if my understandings are correct.
#1077

@evmar
Copy link
Contributor

evmar commented Oct 24, 2018

Igor says that @mhevery is the right person to review this

@evmar
Copy link
Contributor

evmar commented Oct 24, 2018

Also at a higher level, why do we need to reimplement these? Why can't we have our monkeypatches reuse the builtins?

@JiaLiPassion
Copy link
Collaborator

should we add some test cases about this? I can do that in another PR.

@JiaLiPassion
Copy link
Collaborator

@evmar, yeah, I think if we only need to keep the zone information, we can just monkey-patch the constructor and then. I believe the main reason is we want to have our own microTaskQueue, so we can run the microTask inside this queue after other macroTask(setTimeout) is invoked, is that correct, @mhevery?

@trevorade
Copy link
Contributor Author

@mhevery, this appears ready to go. Not sure what the next step is for this.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Promise.all indexing issue when combining non-zone.js promises
5 participants