Skip to content

Commit

Permalink
fix the order of calling reactions of fulfilled / rejected promises i…
Browse files Browse the repository at this point in the history
…n `Promise.prototype.then`, close #1026
  • Loading branch information
zloirock committed Dec 22, 2021
1 parent 09db127 commit e287627
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 40 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
@@ -1,5 +1,6 @@
## Changelog
##### Unreleased
- Fixed the order of calling reactions of fulfilled / rejected promises in `Promise.prototype.then`, [#1026](https://github.com/zloirock/core-js/issues/1026)
- Fixed some missed dependencies of entries
- Added Deno 1.18 compat data mapping

Expand Down
4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -116,9 +116,9 @@
"test-entries-content": "zx tests/commonjs-entries-content.mjs",
"test-entries-standalone": "run-s init test-entries",
"test-targets-parser": "zx tests/targets-parser.mjs",
"test": "run-s init test-lint bundle test-unit test-promises-aplus test-observables test-entries test-targets-parser check",
"test": "run-s init test-lint bundle test-unit test-promises test-observables test-entries test-targets-parser check",
"ci-karma": "run-s init bundle test-unit-karma",
"ci-tests": "run-s init bundle test-unit-node test-promises-aplus test-observables test-entries test-targets-parser",
"ci-tests": "run-s init bundle test-unit-node test-promises test-observables test-entries test-targets-parser",
"clean-dependencies": "node scripts/clean-dependencies.mjs",
"refresh": "npm run clean-dependencies && npm it",
"downloads": "zx scripts/downloads-by-versions.mjs",
Expand Down
82 changes: 44 additions & 38 deletions packages/core-js/modules/es.promise.js
Expand Up @@ -4,6 +4,7 @@ var IS_PURE = require('../internals/is-pure');
var global = require('../internals/global');
var getBuiltIn = require('../internals/get-built-in');
var call = require('../internals/function-call');
var uncurryThis = require('../internals/function-uncurry-this');
var NativePromise = require('../internals/native-promise-constructor');
var redefine = require('../internals/redefine');
var redefineAll = require('../internals/redefine-all');
Expand Down Expand Up @@ -43,6 +44,7 @@ var PromisePrototype = NativePromisePrototype;
var TypeError = global.TypeError;
var document = global.document;
var process = global.process;
var push = uncurryThis([].push);
var newPromiseCapability = newPromiseCapabilityModule.f;
var newGenericPromiseCapability = newPromiseCapability;

Expand Down Expand Up @@ -95,47 +97,50 @@ var isThenable = function (it) {
return isObject(it) && isCallable(then = it.then) ? then : false;
};

var callReaction = function (reaction, state) {
var value = state.value;
var ok = state.state == FULFILLED;
var handler = ok ? reaction.ok : reaction.fail;
var resolve = reaction.resolve;
var reject = reaction.reject;
var domain = reaction.domain;
var result, then, exited;
try {
if (handler) {
if (!ok) {
if (state.rejection === UNHANDLED) onHandleUnhandled(state);
state.rejection = HANDLED;
}
if (handler === true) result = value;
else {
if (domain) domain.enter();
result = handler(value); // can throw
if (domain) {
domain.exit();
exited = true;
}
}
if (result === reaction.promise) {
reject(TypeError('Promise-chain cycle'));
} else if (then = isThenable(result)) {
call(then, result, resolve, reject);
} else resolve(result);
} else reject(value);
} catch (error) {
if (domain && !exited) domain.exit();
reject(error);
}
};

var notify = function (state, isReject) {
if (state.notified) return;
state.notified = true;
var chain = state.reactions;
microtask(function () {
var value = state.value;
var ok = state.state == FULFILLED;
var chain = state.reactions;
var index = 0;
// variable length - can't use forEach
while (chain.length > index) {
var reaction = chain[index++];
var handler = ok ? reaction.ok : reaction.fail;
var resolve = reaction.resolve;
var reject = reaction.reject;
var domain = reaction.domain;
var result, then, exited;
try {
if (handler) {
if (!ok) {
if (state.rejection === UNHANDLED) onHandleUnhandled(state);
state.rejection = HANDLED;
}
if (handler === true) result = value;
else {
if (domain) domain.enter();
result = handler(value); // can throw
if (domain) {
domain.exit();
exited = true;
}
}
if (result === reaction.promise) {
reject(TypeError('Promise-chain cycle'));
} else if (then = isThenable(result)) {
call(then, result, resolve, reject);
} else resolve(result);
} else reject(value);
} catch (error) {
if (domain && !exited) domain.exit();
reject(error);
}
callReaction(chain[index++], state);
}
state.reactions = [];
state.notified = false;
Expand Down Expand Up @@ -265,14 +270,15 @@ if (FORCED) {
// https://tc39.es/ecma262/#sec-promise.prototype.then
then: function then(onFulfilled, onRejected) {
var state = getInternalPromiseState(this);
var reactions = state.reactions;
var reaction = newPromiseCapability(speciesConstructor(this, PromiseConstructor));
state.parent = true;
reaction.ok = isCallable(onFulfilled) ? onFulfilled : true;
reaction.fail = isCallable(onRejected) && onRejected;
reaction.domain = IS_NODE ? process.domain : undefined;
state.parent = true;
reactions[reactions.length] = reaction;
if (state.state != PENDING) notify(state, false);
if (state.state == PENDING) push(state.reactions, reaction);
else microtask(function () {
callReaction(reaction, state);
});
return reaction.promise;
},
// `Promise.prototype.catch` method
Expand Down

0 comments on commit e287627

Please sign in to comment.