mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 941920 - Implement full Promise API in Promise.jsm. r=paolo
This commit is contained in:
parent
5d81933f75
commit
118b2ca6d7
@ -340,6 +340,21 @@ Promise.prototype.then = function (aOnResolve, aOnReject)
|
||||
return handler.nextPromise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Invokes `promise.then` with undefined for the resolve handler and a given
|
||||
* reject handler.
|
||||
*
|
||||
* @param aOnReject
|
||||
* The rejection handler.
|
||||
*
|
||||
* @return A new pending promise returned.
|
||||
*
|
||||
* @see Promise.prototype.then
|
||||
*/
|
||||
Promise.prototype.catch = function (aOnReject)
|
||||
{
|
||||
return this.then(undefined, aOnReject);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new pending promise and provides methods to resolve or reject it.
|
||||
@ -375,6 +390,10 @@ Promise.resolve = function (aValue)
|
||||
"the Task and return its promise.");
|
||||
}
|
||||
|
||||
if (aValue instanceof Promise) {
|
||||
return aValue;
|
||||
}
|
||||
|
||||
return new Promise((aResolve) => aResolve(aValue));
|
||||
};
|
||||
|
||||
@ -419,40 +438,62 @@ Promise.all = function (aValues)
|
||||
throw new Error("Promise.all() expects an iterable.");
|
||||
}
|
||||
|
||||
if (!Array.isArray(aValues)) {
|
||||
aValues = [...aValues];
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let values = Array.isArray(aValues) ? aValues : [...aValues];
|
||||
let countdown = values.length;
|
||||
let resolutionValues = new Array(countdown);
|
||||
|
||||
if (!aValues.length) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
let countdown = aValues.length;
|
||||
let deferred = Promise.defer();
|
||||
let resolutionValues = new Array(countdown);
|
||||
|
||||
function checkForCompletion(aValue, aIndex) {
|
||||
resolutionValues[aIndex] = aValue;
|
||||
|
||||
if (--countdown === 0) {
|
||||
deferred.resolve(resolutionValues);
|
||||
if (!countdown) {
|
||||
resolve(resolutionValues);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < aValues.length; i++) {
|
||||
let index = i;
|
||||
let value = aValues[i];
|
||||
let resolve = val => checkForCompletion(val, index);
|
||||
|
||||
if (value && typeof(value.then) == "function") {
|
||||
value.then(resolve, deferred.reject);
|
||||
} else {
|
||||
// Given value is not a promise, forward it as a resolution value.
|
||||
resolve(value);
|
||||
function checkForCompletion(aValue, aIndex) {
|
||||
resolutionValues[aIndex] = aValue;
|
||||
if (--countdown === 0) {
|
||||
resolve(resolutionValues);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
let index = i;
|
||||
let value = values[i];
|
||||
let resolver = val => checkForCompletion(val, index);
|
||||
|
||||
if (value && typeof(value.then) == "function") {
|
||||
value.then(resolver, reject);
|
||||
} else {
|
||||
// Given value is not a promise, forward it as a resolution value.
|
||||
resolver(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a promise that is resolved or rejected when the first value is
|
||||
* resolved or rejected, taking on the value or reason of that promise.
|
||||
*
|
||||
* @param aValues
|
||||
* Iterable of values or promises that may be pending, resolved, or
|
||||
* rejected. When any is resolved or rejected, the returned promise will
|
||||
* be resolved or rejected as to the given value or reason.
|
||||
*
|
||||
* @return A new promise that is fulfilled when any values are resolved or
|
||||
* rejected. Its resolution value will be forwarded from the resolution
|
||||
* value or rejection reason.
|
||||
*/
|
||||
Promise.race = function (aValues)
|
||||
{
|
||||
if (aValues == null || typeof(aValues["@@iterator"]) != "function") {
|
||||
throw new Error("Promise.race() expects an iterable.");
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
return new Promise((resolve, reject) => {
|
||||
for (let value of aValues) {
|
||||
Promise.resolve(value).then(resolve, reject);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Object.freeze(Promise);
|
||||
|
@ -84,7 +84,7 @@ let tests = [];
|
||||
// This function is useful if the promise itself is
|
||||
// not returned.
|
||||
let observe_failures = function observe_failures(promise) {
|
||||
promise.then(null, function onReject(reason) {
|
||||
promise.catch(function onReject(reason) {
|
||||
test.do_throw("Observed failure in test " + test + ": " + reason);
|
||||
});
|
||||
};
|
||||
@ -404,8 +404,7 @@ tests.push(
|
||||
}
|
||||
);
|
||||
|
||||
promise = promise.then(
|
||||
null,
|
||||
promise = promise.catch(
|
||||
function onReject(reason) {
|
||||
do_check_eq(reason, boom2, "Rejection was propagated with the correct " +
|
||||
"reason, through a promise");
|
||||
@ -436,7 +435,7 @@ tests.push(
|
||||
);
|
||||
}));
|
||||
|
||||
// Test sequences of |then|
|
||||
// Test sequences of |then| and |catch|
|
||||
tests.push(
|
||||
make_promise_test(function test_chaining(test) {
|
||||
let error_1 = new Error("Error 1");
|
||||
@ -461,8 +460,7 @@ tests.push(
|
||||
);
|
||||
|
||||
// Check that returning from the promise produces a resolution
|
||||
promise = promise.then(
|
||||
null,
|
||||
promise = promise.catch(
|
||||
function onReject() {
|
||||
do_throw("Incorrect rejection");
|
||||
}
|
||||
@ -488,16 +486,14 @@ tests.push(
|
||||
}
|
||||
);
|
||||
|
||||
promise = promise.then(
|
||||
null,
|
||||
promise = promise.catch(
|
||||
function onReject(reason) {
|
||||
do_check_true(reason == error_1, "Reason was propagated correctly");
|
||||
throw error_2;
|
||||
}
|
||||
);
|
||||
|
||||
promise = promise.then(
|
||||
null,
|
||||
promise = promise.catch(
|
||||
function onReject(reason) {
|
||||
do_check_true(reason == error_2, "Throwing an error altered the reason " +
|
||||
"as expected");
|
||||
@ -538,12 +534,15 @@ tests.push(
|
||||
tests.push(
|
||||
make_promise_test(function test_resolve(test) {
|
||||
const RESULT = "arbitrary value";
|
||||
let promise = Promise.resolve(RESULT).then(
|
||||
let p1 = Promise.resolve(RESULT);
|
||||
let p2 = Promise.resolve(p1);
|
||||
do_check_eq(p1, p2, "Promise.resolve used on a promise just returns the promise");
|
||||
|
||||
return p1.then(
|
||||
function onResolve(result) {
|
||||
do_check_eq(result, RESULT, "Promise.resolve propagated the correct result");
|
||||
}
|
||||
);
|
||||
return promise;
|
||||
}));
|
||||
|
||||
// Test that Promise.resolve throws when its argument is an async function.
|
||||
@ -667,9 +666,9 @@ tests.push(
|
||||
make_promise_test(function all_resolve_no_promises(test) {
|
||||
try {
|
||||
Promise.all(null);
|
||||
do_check_true(false, "all() should only accept arrays.");
|
||||
do_check_true(false, "all() should only accept iterables");
|
||||
} catch (e) {
|
||||
do_check_true(true, "all() fails when first the arg is not an array.");
|
||||
do_check_true(true, "all() fails when first the arg is not an iterable");
|
||||
}
|
||||
|
||||
let p1 = Promise.all([]).then(
|
||||
@ -689,6 +688,148 @@ tests.push(
|
||||
return Promise.all([p1, p2]);
|
||||
}));
|
||||
|
||||
// Test that Promise.all() handles non-array iterables
|
||||
tests.push(
|
||||
make_promise_test(function all_iterable(test) {
|
||||
function* iterable() {
|
||||
yield 1;
|
||||
yield 2;
|
||||
yield 3;
|
||||
}
|
||||
|
||||
return Promise.all(iterable()).then(
|
||||
function onResolve([val1, val2, val3]) {
|
||||
do_check_eq(val1, 1);
|
||||
do_check_eq(val2, 2);
|
||||
do_check_eq(val3, 3);
|
||||
},
|
||||
function onReject() {
|
||||
do_throw("all() unexpectedly rejected");
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
// Test that throwing from the iterable passed to Promise.all() rejects the
|
||||
// promise returned by Promise.all()
|
||||
tests.push(
|
||||
make_promise_test(function all_iterable_throws(test) {
|
||||
function* iterable() {
|
||||
throw 1;
|
||||
}
|
||||
|
||||
return Promise.all(iterable()).then(
|
||||
function onResolve() {
|
||||
do_throw("all() unexpectedly resolved");
|
||||
},
|
||||
function onReject(reason) {
|
||||
do_check_eq(reason, 1, "all() rejects when the iterator throws");
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
// Test that Promise.race() resolves with the first available resolution value
|
||||
tests.push(
|
||||
make_promise_test(function race_resolve(test) {
|
||||
let p1 = Promise.resolve(1);
|
||||
let p2 = Promise.resolve().then(() => 2);
|
||||
|
||||
return Promise.race([p1, p2]).then(
|
||||
function onResolve(value) {
|
||||
do_check_eq(value, 1);
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
// Test that passing only values (not promises) to Promise.race() works
|
||||
tests.push(
|
||||
make_promise_test(function race_resolve_no_promises(test) {
|
||||
try {
|
||||
Promise.race(null);
|
||||
do_check_true(false, "race() should only accept iterables");
|
||||
} catch (e) {
|
||||
do_check_true(true, "race() fails when first the arg is not an iterable");
|
||||
}
|
||||
|
||||
return Promise.race([1, 2, 3]).then(
|
||||
function onResolve(value) {
|
||||
do_check_eq(value, 1);
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
// Test that Promise.race() never resolves when passed an empty iterable
|
||||
tests.push(
|
||||
make_promise_test(function race_resolve_never(test) {
|
||||
return new Promise(resolve => {
|
||||
Promise.race([]).then(
|
||||
function onResolve() {
|
||||
do_throw("race() unexpectedly resolved");
|
||||
},
|
||||
function onReject() {
|
||||
do_throw("race() unexpectedly rejected");
|
||||
}
|
||||
);
|
||||
|
||||
// Approximate "never" so we don't have to solve the halting problem.
|
||||
do_timeout(200, resolve);
|
||||
});
|
||||
}));
|
||||
|
||||
// Test that Promise.race() handles non-array iterables.
|
||||
tests.push(
|
||||
make_promise_test(function race_iterable(test) {
|
||||
function* iterable() {
|
||||
yield 1;
|
||||
yield 2;
|
||||
yield 3;
|
||||
}
|
||||
|
||||
return Promise.race(iterable()).then(
|
||||
function onResolve(value) {
|
||||
do_check_eq(value, 1);
|
||||
},
|
||||
function onReject() {
|
||||
do_throw("race() unexpectedly rejected");
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
// Test that throwing from the iterable passed to Promise.race() rejects the
|
||||
// promise returned by Promise.race()
|
||||
tests.push(
|
||||
make_promise_test(function race_iterable_throws(test) {
|
||||
function* iterable() {
|
||||
throw 1;
|
||||
}
|
||||
|
||||
return Promise.race(iterable()).then(
|
||||
function onResolve() {
|
||||
do_throw("race() unexpectedly resolved");
|
||||
},
|
||||
function onReject(reason) {
|
||||
do_check_eq(reason, 1, "race() rejects when the iterator throws");
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
// Test that rejecting one of the promises passed to Promise.race() rejects the
|
||||
// promise returned by Promise.race()
|
||||
tests.push(
|
||||
make_promise_test(function race_reject(test) {
|
||||
let p1 = Promise.reject(1);
|
||||
let p2 = Promise.resolve(2);
|
||||
let p3 = Promise.resolve(3);
|
||||
|
||||
return Promise.race([p1, p2, p3]).then(
|
||||
function onResolve() {
|
||||
do_throw("race() unexpectedly resolved");
|
||||
},
|
||||
function onReject(reason) {
|
||||
do_check_eq(reason, 1, "race() rejects when given a rejected promise");
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
// Test behavior of the Promise constructor.
|
||||
tests.push(
|
||||
make_promise_test(function test_constructor(test) {
|
||||
@ -803,7 +944,7 @@ tests.push(
|
||||
}, null);
|
||||
|
||||
do_print("Setting wait for second promise");
|
||||
return promise2.then(null, error => {return 3;})
|
||||
return promise2.catch(error => {return 3;})
|
||||
.then(
|
||||
count => {
|
||||
shouldExitNestedEventLoop = true;
|
||||
@ -930,7 +1071,7 @@ make_promise_test(function test_caught_is_not_reported() {
|
||||
let promise = wait_for_uncaught([salt], 500);
|
||||
(function() {
|
||||
let uncaught = Promise.reject("This error, on the other hand, is caught " + salt);
|
||||
uncaught.then(null, function() { /* ignore rejection */});
|
||||
uncaught.catch(function() { /* ignore rejection */});
|
||||
uncaught = null;
|
||||
})();
|
||||
// Isolate this in a function to increase likelihood that the gc will
|
||||
|
Loading…
Reference in New Issue
Block a user