backport await dictionary stage 3 proposal

This commit is contained in:
Denis Pushkarev
2026-07-25 15:27:40 +03:00
parent f329da8bae
commit 150592f0e1
23 changed files with 961 additions and 0 deletions
+4
View File
@@ -19,6 +19,10 @@
- Added [`Iterator` join stage 3 proposal](https://github.com/tc39/proposal-iterator-join):
- Added built-in:
- `Iterator.prototype.join`
- Added [Await dictionary of Promises stage 3 proposal](https://github.com/tc39/proposal-await-dictionary):
- Added built-ins:
- `Promise.allKeyed`
- `Promise.allSettledKeyed`
- Throw a `RangeError` on finite unsafe integer `limit` in `Iterator.prototype.{ drop, take }`, following [tc39/ecma262/#3776](https://github.com/tc39/ecma262/pull/3776)
- Deno 2.9+ [*replaces* `Object.prototype.__proto__`](https://github.com/denoland/deno/pull/35192) instead of removing it, so the feature detection updated
- Ensure opaque paths always roundtrip in URL polyfill (still without adding to feature detection), [whatwg/url#844](https://github.com/whatwg/url/pull/844)
+30
View File
@@ -175,6 +175,7 @@ structuredClone(new Set([1, 2, 3])); // => new Set([1, 2, 3])
- [`Iterator` chunking](#iterator-chunking)
- [`Iterator` includes](#iterator-includes)
- [`Iterator` join](#iterator-join)
- [Await dictionary](#await-dictionary)
- [Stage 2.7 proposals](#stage-27-proposals)
- [`Symbol.metadata` for decorators metadata proposal](#symbolmetadata-for-decorators-metadata-proposal)
- [Stage 2 proposals](#stage-2-proposals)
@@ -2941,6 +2942,35 @@ core-js(-pure)/actual|full/iterator/join
[1, null, 3].values().join(); // => '1,,3'
```
##### [Await dictionary](https://github.com/tc39/proposal-await-dictionary)[⬆](#index)
Modules [`esnext.promise.all-keyed`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.promise.all-keyed.js)
and [`esnext.promise.all-settled-keyed`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.promise.all-settled-keyed.js)
```ts
class Promise {
allKeyed(obj: Record<string | symbol, any>): Promise<Record<string | symbol, any>>;
allSettledKeyed(obj: Record<string | symbol, any>): Promise<Record<string | symbol, { status: 'fulfilled', value: any } | { status: 'rejected', reason: any }>>;
}
```
[*CommonJS entry points:*](#commonjs-api)
```
core-js/proposals/await-dictionary
core-js(-pure)/actual|full/promise/all-keyed
core-js(-pure)/actual|full/promise/all-settled-keyed
```
```js
await Promise.allKeyed({
a: Promise.resolve(1),
b: Promise.resolve(2),
c: 3,
}); // => { a: 1, b: 2, c: 3 }
await Promise.allSettledKeyed({
a: Promise.resolve(1),
b: Promise.reject(2),
c: 3,
}); // => { a: { status: "fulfilled", value: 1 }, b: { status: "rejected", reason: 2 }, c: { status: "fulfilled", value: 3 } }
```
#### Stage 2.7 proposals[⬆](#index)
[*CommonJS entry points:*](#commonjs-api)
```
@@ -0,0 +1,41 @@
# Await dictionary
[Specification](https://tc39.es/proposal-await-dictionary/)\
[Proposal repo](https://github.com/tc39/proposal-await-dictionary)
## Modules
[`esnext.promise.all-keyed`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.promise.all-keyed.js), [`esnext.promise.all-settled-keyed`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.promise.all-settled-keyed.js)
## Built-ins signatures
```ts
class Promise {
allKeyed<T extends Record<string, unknown>>(
obj: T
): Promise<{ [K in keyof T]: Awaited<T[K]> }>;
allSettledKeyed<T extends Record<string, unknown>>(
obj: T
): Promise<{ [K in keyof T]: PromiseSettledResult<Awaited<T[K]>> }>;
}
```
## [Entry points]({docs-version}/docs/usage#h-entry-points)
```plaintext
core-js/proposals/await-dictionary
core-js(-pure)/actual|full/promise/all-keyed
core-js(-pure)/actual|full/promise/all-settled-keyed
```
## Examples
```js
await Promise.allKeyed({
a: Promise.resolve(1),
b: Promise.resolve(2),
c: 3,
}); // => { a: 1, b: 2, c: 3 }
await Promise.allSettledKeyed({
a: Promise.resolve(1),
b: Promise.reject(2),
c: 3,
}); // => { a: { status: "fulfilled", value: 1 }, b: { status: "rejected", reason: 2 }, c: { status: "fulfilled", value: 3 } }
```
+4
View File
@@ -254,6 +254,10 @@
{
"title": "Stage 3 proposals",
"children": [
{
"title": "Await dictionary",
"url": "{docs-version}/docs/features/proposals/await-dictionary"
},
{
"title": "Iterator chunking",
"url": "{docs-version}/docs/features/proposals/iterator-chunking"
+4
View File
@@ -2786,8 +2786,12 @@ export const data = {
},
'esnext.observable.of': {
},
'esnext.promise.all-keyed': {
},
// TODO: Remove from `core-js@4`
'esnext.promise.all-settled': null,
'esnext.promise.all-settled-keyed': {
},
// TODO: Remove from `core-js@4`
'esnext.promise.any': null,
// TODO: Remove from `core-js@4`
@@ -328,5 +328,7 @@ export default {
'es.iterator.zip-keyed',
'esnext.iterator.includes',
'esnext.iterator.join',
'esnext.promise.all-keyed',
'esnext.promise.all-settled-keyed',
],
};
@@ -0,0 +1,16 @@
'use strict';
require('../../modules/es.object.create');
require('../../modules/es.object.to-string');
require('../../modules/es.promise');
require('../../modules/es.reflect.own-keys');
require('../../modules/esnext.promise.all-keyed');
var call = require('../../internals/function-call');
var isCallable = require('../../internals/is-callable');
var path = require('../../internals/path');
var Promise = path.Promise;
var $allKeyed = Promise.allKeyed;
module.exports = function allKeyed(promises) {
return call($allKeyed, isCallable(this) ? this : Promise, promises);
};
@@ -0,0 +1,16 @@
'use strict';
require('../../modules/es.object.create');
require('../../modules/es.object.to-string');
require('../../modules/es.promise');
require('../../modules/es.reflect.own-keys');
require('../../modules/esnext.promise.all-settled-keyed');
var call = require('../../internals/function-call');
var isCallable = require('../../internals/is-callable');
var path = require('../../internals/path');
var Promise = path.Promise;
var $allSettledKeyed = Promise.allSettledKeyed;
module.exports = function allSettledKeyed(promises) {
return call($allSettledKeyed, isCallable(this) ? this : Promise, promises);
};
+4
View File
@@ -1,5 +1,9 @@
'use strict';
var parent = require('../../stable/promise');
require('../../modules/es.object.create');
require('../../modules/es.reflect.own-keys');
require('../../modules/esnext.promise.all-keyed');
require('../../modules/esnext.promise.all-settled-keyed');
// TODO: Remove from `core-js@4`
require('../../modules/esnext.promise.try');
require('../../modules/esnext.promise.with-resolvers');
@@ -0,0 +1,4 @@
'use strict';
var parent = require('../../actual/promise/all-keyed');
module.exports = parent;
@@ -0,0 +1,4 @@
'use strict';
var parent = require('../../actual/promise/all-settled-keyed');
module.exports = parent;
@@ -0,0 +1,57 @@
'use strict';
var $ = require('../internals/export');
var aCallable = require('../internals/a-callable');
var anObject = require('../internals/an-object');
var call = require('../internals/function-call');
var createProperty = require('../internals/create-property');
var getBuiltIn = require('../internals/get-built-in');
var getOwnPropertyDescriptor = require('../internals/object-get-own-property-descriptor');
var newPromiseCapabilityModule = require('../internals/new-promise-capability');
var perform = require('../internals/perform');
var create = getBuiltIn('Object', 'create');
var ownKeys = getBuiltIn('Reflect', 'ownKeys');
// `Promise.allKeyed` method
// https://github.com/tc39/proposal-await-dictionary
$({ target: 'Promise', stat: true }, {
allKeyed: function allKeyed(promises) {
var C = this;
var capability = newPromiseCapabilityModule.f(C);
var resolve = capability.resolve;
var reject = capability.reject;
var result = perform(function () {
var promiseResolve = aCallable(C.resolve);
var allKeys = ownKeys(anObject(promises));
var keys = [];
var values = [];
var remaining = 1;
var counter = 0;
for (var i = 0; i < allKeys.length; i++) (function (key) {
var desc = getOwnPropertyDescriptor.f(promises, key);
if (desc && desc.enumerable) {
var index = counter;
var alreadyCalled = false;
remaining++;
keys[index] = key;
values[index] = undefined;
call(promiseResolve, C, promises[key]).then(function (value) {
if (alreadyCalled) return;
alreadyCalled = true;
values[index] = value;
--remaining;
if (remaining === 0) {
var res = create(null);
for (var j = 0; j < keys.length; j++) createProperty(res, keys[j], values[j]);
resolve(res);
}
}, reject);
counter++;
}
})(allKeys[i]);
--remaining || resolve(create(null));
});
if (result.error) reject(result.value);
return capability.promise;
}
});
@@ -0,0 +1,60 @@
'use strict';
var $ = require('../internals/export');
var aCallable = require('../internals/a-callable');
var anObject = require('../internals/an-object');
var call = require('../internals/function-call');
var createProperty = require('../internals/create-property');
var getBuiltIn = require('../internals/get-built-in');
var getOwnPropertyDescriptor = require('../internals/object-get-own-property-descriptor');
var newPromiseCapabilityModule = require('../internals/new-promise-capability');
var perform = require('../internals/perform');
var create = getBuiltIn('Object', 'create');
var ownKeys = getBuiltIn('Reflect', 'ownKeys');
// `Promise.allSettledKeyed` method
// https://github.com/tc39/proposal-await-dictionary
$({ target: 'Promise', stat: true }, {
allSettledKeyed: function allSettledKeyed(promises) {
var C = this;
var capability = newPromiseCapabilityModule.f(C);
var resolve = capability.resolve;
var reject = capability.reject;
var result = perform(function () {
var promiseResolve = aCallable(C.resolve);
var allKeys = ownKeys(anObject(promises));
var keys = [];
var values = [];
var remaining = 1;
var counter = 0;
for (var i = 0; i < allKeys.length; i++) (function (key) {
var desc = getOwnPropertyDescriptor.f(promises, key);
if (desc && desc.enumerable) {
var createElementResolver = function (rejection) {
return function (value) {
if (alreadyCalled) return;
alreadyCalled = true;
values[index] = rejection
? { status: 'rejected', reason: value }
: { status: 'fulfilled', value: value };
if (--remaining) return;
var res = create(null);
for (var j = 0; j < keys.length; j++) createProperty(res, keys[j], values[j]);
resolve(res);
};
};
var index = counter;
var alreadyCalled = false;
remaining++;
keys[index] = key;
values[index] = undefined;
call(promiseResolve, C, promises[key]).then(createElementResolver(false), createElementResolver(true));
counter++;
}
})(allKeys[i]);
--remaining || resolve(create(null));
});
if (result.error) reject(result.value);
return capability.promise;
}
});
@@ -0,0 +1,4 @@
'use strict';
// https://github.com/tc39/proposal-await-dictionary
require('../modules/esnext.promise.all-keyed');
require('../modules/esnext.promise.all-settled-keyed');
+1
View File
@@ -1,6 +1,7 @@
'use strict';
var parent = require('./4');
require('../proposals/await-dictionary');
require('../proposals/decorator-metadata-v2');
require('../proposals/iterator-includes');
require('../proposals/iterator-join');
+6
View File
@@ -2068,6 +2068,12 @@ GLOBAL.tests = {
'esnext.set.add-all': function () {
return Set.prototype.addAll;
},
'esnext.promise.all-keyed': function () {
return Promise.allKeyed;
},
'esnext.promise.all-settled-keyed': function () {
return Promise.allSettledKeyed;
},
'esnext.set.delete-all': function () {
return Set.prototype.deleteAll;
},
+3
View File
@@ -725,6 +725,8 @@ for (PATH of ['core-js-pure', 'core-js']) {
ok(load(NS, 'iterator/join')([1, 2].values()) === '1,2');
ok(typeof load(NS, 'iterator/to-async') == 'function');
ok(typeof load(NS, 'iterator/windows') == 'function');
ok(load(NS, 'promise/all-keyed')({}) instanceof Promise);
ok(load(NS, 'promise/all-settled-keyed')({}) instanceof Promise);
ok(load(NS, 'symbol/metadata'));
const instanceGroup = load(NS, 'instance/group');
@@ -933,6 +935,7 @@ for (PATH of ['core-js-pure', 'core-js']) {
load('proposals/async-explicit-resource-management');
load('proposals/async-iteration');
load('proposals/async-iterator-helpers');
load('proposals/await-dictionary');
load('proposals/change-array-by-copy');
load('proposals/change-array-by-copy-stage-4');
load('proposals/collection-methods');
+4
View File
@@ -1364,6 +1364,10 @@ const forbidCompletelyNonExistentBuiltIns = {
'iterateKeys',
'iterateValues',
] }],
'es/no-nonstandard-promise-properties': [ERROR, { allow: [
'allKeyed',
'allSettledKeyed',
] }],
'es/no-nonstandard-reflect-properties': [ERROR, { allow: [
// TODO: drop from `core-js@4`
'defineMetadata',
+13
View File
@@ -1,4 +1,5 @@
import Promise from 'core-js-pure/es/promise';
import create from 'core-js-pure/es/object/create';
import ITERATOR from 'core-js-pure/es/symbol/iterator';
import ASYNC_ITERATOR from 'core-js-pure/es/symbol/async-iterator';
@@ -7,6 +8,18 @@ export function is(a, b) {
return a === b ? a !== 0 || 1 / a === 1 / b : a !== a && b !== b;
}
// null-proto copy of the expected object: `deepEqual` of a null-proto result against
// a plain literal is sensitive to prototype identity in some QUnit versions / realms
export function nullProto(source) {
// plain copy loop: helpers run inside the unit-pure bundles too, where the modern
// standard library must not be touched
const result = create(null);
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) result[key] = source[key];
}
return result;
}
export function createIterator(elements, methods) {
let index = 0;
const iterator = {
@@ -0,0 +1,156 @@
QUnit.test('Promise.allKeyed', assert => {
assert.isFunction(Promise.allKeyed);
assert.arity(Promise.allKeyed, 1);
assert.looksNative(Promise.allKeyed);
assert.nonEnumerable(Promise, 'allKeyed');
assert.true(Promise.allKeyed({}) instanceof Promise, 'returns a promise');
});
QUnit.test('Promise.allKeyed, resolved with primitives', assert => {
return Promise.allKeyed({
a: 1,
b: Promise.resolve(2),
c: 3,
}).then(it => {
assert.deepEqual(it, {
a: 1,
b: 2,
c: 3,
}, 'resolved with a correct value');
});
});
QUnit.test('Promise.allKeyed, resolved', assert => {
return Promise.allKeyed({
a: Promise.resolve(1),
b: Promise.resolve(2),
c: Promise.resolve(3),
}).then(it => {
assert.deepEqual(it, {
a: 1,
b: 2,
c: 3,
}, 'resolved with a correct value');
});
});
QUnit.test('Promise.allKeyed, resolved with rejection', assert => {
return Promise.allKeyed({
a: Promise.resolve(1),
b: Promise.reject(2),
c: Promise.resolve(3),
}).then(() => {
assert.avoid();
}, error => {
assert.same(error, 2, 'rejected with a correct value');
});
});
QUnit.test('Promise.allKeyed, resolved with empty object', assert => {
return Promise.allKeyed({}).then(it => {
assert.deepEqual(it, {}, 'resolved with a correct value');
});
});
QUnit.test('Promise.allKeyed, resolved with hidden attributes', assert => {
const obj = Object.create({ proto: Promise.resolve('hidden') });
obj.visible = Promise.resolve(42);
Object.defineProperty(obj, 'invisible', { value: Promise.resolve(99), enumerable: false });
return Promise.allKeyed(obj).then(it => {
assert.deepEqual(it, { visible: 42 }, 'ignores prototype/invisible');
});
});
QUnit.test('Promise.allKeyed, rejected on incorrect input', assert => {
return Promise.allKeyed('string').then(() => {
assert.avoid();
}, error => {
assert.true(error instanceof TypeError, 'error is TypeError');
assert.required('rejected as expected');
});
});
QUnit.test('Promise.allKeyed, resolved with timeouts', assert => {
return Promise.allKeyed({
a: Promise.resolve(1),
b: new Promise(resolve => setTimeout(() => resolve(2), 10)),
c: Promise.resolve(3),
}).then(it => {
assert.deepEqual(it, {
a: 1,
b: 2,
c: 3,
}, 'keeps correct mapping, even with delays');
});
});
QUnit.test('Promise.allKeyed, subclassing', assert => {
const { allKeyed, resolve } = Promise;
function SubPromise(executor) {
executor(() => { /* empty */ }, () => { /* empty */ });
}
SubPromise.resolve = resolve.bind(Promise);
assert.true(allKeyed.call(SubPromise, { a: Promise.resolve(1) }) instanceof SubPromise, 'subclassing, `this` pattern');
function FakePromise1() { /* empty */ }
function FakePromise2(executor) {
executor(null, () => { /* empty */ });
}
function FakePromise3(executor) {
executor(() => { /* empty */ }, null);
}
FakePromise1.resolve = FakePromise2.resolve = FakePromise3.resolve = resolve.bind(Promise);
assert.throws(() => {
allKeyed.call(FakePromise1, { a: Promise.resolve(1) });
}, 'NewPromiseCapability validations, #1');
assert.throws(() => {
allKeyed.call(FakePromise2, { a: Promise.resolve(1) });
}, 'NewPromiseCapability validations, #2');
assert.throws(() => {
allKeyed.call(FakePromise3, { a: Promise.resolve(1) });
}, 'NewPromiseCapability validations, #3');
});
QUnit.test('Promise.allKeyed, without constructor context', assert => {
const { allKeyed } = Promise;
assert.throws(() => allKeyed({ a: Promise.resolve(1) }), TypeError, 'Throws if called without a constructor context');
assert.throws(() => allKeyed.call(null, { a: Promise.resolve(1) }), TypeError, 'Throws if called with null as this');
});
QUnit.test('Promise.allKeyed, result object has null prototype', assert => {
return Promise.allKeyed({
a: Promise.resolve(1),
b: Promise.resolve(2),
}).then(result => {
assert.strictEqual(
Object.getPrototypeOf(result),
null,
'Result object has null prototype',
);
});
});
QUnit.test('Promise.allKeyed, symbol keys', assert => {
const symA = Symbol('A');
const symB = Symbol('B');
return Promise.allKeyed({
a: Promise.resolve(1),
[symA]: Promise.resolve(2),
[symB]: Promise.resolve(3),
}).then(it => {
assert.same(it.a, 1, 'string key');
assert.same(it[symA], 2, 'symbol key A');
assert.same(it[symB], 3, 'symbol key B');
});
});
QUnit.test('Promise.allKeyed, keys order', assert => {
return Promise.allKeyed({
a: Promise.resolve(1),
b: new Promise(resolve => setTimeout(() => resolve(2), 10)),
c: Promise.resolve(3),
}).then(it => {
const actualKeys = Object.keys(it);
assert.deepEqual(actualKeys, ['a', 'b', 'c'], 'correct order in the case when promises resolves in different order');
});
});
@@ -0,0 +1,166 @@
QUnit.test('Promise.allSettledKeyed', assert => {
assert.isFunction(Promise.allSettledKeyed);
assert.arity(Promise.allSettledKeyed, 1);
assert.looksNative(Promise.allSettledKeyed);
assert.nonEnumerable(Promise, 'allSettledKeyed');
assert.true(Promise.allSettledKeyed({}) instanceof Promise, 'returns a promise');
});
QUnit.test('Promise.allSettledKeyed, resolved', assert => {
return Promise.allSettledKeyed({
a: Promise.resolve(1),
b: Promise.resolve(2),
c: Promise.resolve(3),
}).then(it => {
assert.deepEqual(it, {
a: { value: 1, status: 'fulfilled' },
b: { value: 2, status: 'fulfilled' },
c: { value: 3, status: 'fulfilled' },
}, 'resolved with a correct value');
});
});
QUnit.test('Promise.allSettledKeyed, resolved with primitives', assert => {
return Promise.allSettledKeyed({
a: 1,
b: Promise.resolve(2),
c: 3,
}).then(it => {
assert.deepEqual(it, {
a: { value: 1, status: 'fulfilled' },
b: { value: 2, status: 'fulfilled' },
c: { value: 3, status: 'fulfilled' },
}, 'resolved with a correct value');
});
});
QUnit.test('Promise.allSettledKeyed, resolved with rejection', assert => {
return Promise.allSettledKeyed({
a: Promise.resolve(1),
b: Promise.reject(2),
c: Promise.resolve(3),
}).then(it => {
assert.deepEqual(it, {
a: { value: 1, status: 'fulfilled' },
b: { reason: 2, status: 'rejected' },
c: { value: 3, status: 'fulfilled' },
}, 'resolved with a correct value');
});
});
QUnit.test('Promise.allSettledKeyed, rejected', assert => {
return Promise.allSettledKeyed().then(() => {
assert.avoid();
}, () => {
assert.required('rejected as expected');
});
});
QUnit.test('Promise.allSettledKeyed, resolved with empty object', assert => {
return Promise.allSettledKeyed({}).then(it => {
assert.deepEqual(it, {}, 'resolved with a correct value');
});
});
QUnit.test('Promise.allSettledKeyed, resolved with hidden attributes', assert => {
const obj = Object.create({ proto: Promise.resolve('hidden') });
obj.visible = Promise.resolve(42);
Object.defineProperty(obj, 'invisible', { value: Promise.resolve(99), enumerable: false });
return Promise.allSettledKeyed(obj).then(it => {
assert.deepEqual(it, { visible: { status: 'fulfilled', value: 42 } }, 'ignores prototype/invisible');
});
});
QUnit.test('Promise.allSettledKeyed, rejected on incorrect input', assert => {
return Promise.allSettledKeyed('string').then(() => {
assert.avoid();
}, error => {
assert.true(error instanceof TypeError, 'error is TypeError');
assert.required('rejected as expected');
});
});
QUnit.test('Promise.allSettledKeyed, resolved with timeouts', assert => {
return Promise.allSettledKeyed({
a: Promise.resolve(1),
b: new Promise(resolve => setTimeout(() => resolve(2), 10)),
c: Promise.resolve(3),
}).then(it => {
assert.deepEqual(it, {
a: { value: 1, status: 'fulfilled' },
b: { value: 2, status: 'fulfilled' },
c: { value: 3, status: 'fulfilled' },
}, 'keeps correct mapping, even with delays');
});
});
QUnit.test('Promise.allSettledKeyed, subclassing', assert => {
const { allSettledKeyed, resolve } = Promise;
function SubPromise(executor) {
executor(() => { /* empty */ }, () => { /* empty */ });
}
SubPromise.resolve = resolve.bind(Promise);
assert.true(allSettledKeyed.call(SubPromise, { a: 1, b: 2, c: 3 }) instanceof SubPromise, 'subclassing, `this` pattern');
function FakePromise1() { /* empty */ }
function FakePromise2(executor) {
executor(null, () => { /* empty */ });
}
function FakePromise3(executor) {
executor(() => { /* empty */ }, null);
}
FakePromise1.resolve = FakePromise2.resolve = FakePromise3.resolve = resolve.bind(Promise);
assert.throws(() => {
allSettledKeyed.call(FakePromise1, { a: 1, b: 2, c: 3 });
}, 'NewPromiseCapability validations, #1');
assert.throws(() => {
allSettledKeyed.call(FakePromise2, { a: 1, b: 2, c: 3 });
}, 'NewPromiseCapability validations, #2');
assert.throws(() => {
allSettledKeyed.call(FakePromise3, { a: 1, b: 2, c: 3 });
}, 'NewPromiseCapability validations, #3');
});
QUnit.test('Promise.allSettledKeyed, without constructor context', assert => {
const { allSettledKeyed } = Promise;
assert.throws(() => allSettledKeyed({ a: Promise.resolve(1) }), TypeError, 'Throws if called without a constructor context');
assert.throws(() => allSettledKeyed.call(null, { a: Promise.resolve(1) }), TypeError, 'Throws if called with null as this');
});
QUnit.test('Promise.allSettledKeyed, result object has null prototype', assert => {
return Promise.allSettledKeyed({
a: Promise.resolve(1),
b: Promise.resolve(2),
}).then(result => {
assert.strictEqual(
Object.getPrototypeOf(result),
null,
'Result object has null prototype',
);
});
});
QUnit.test('Promise.allSettledKeyed, symbol keys', assert => {
const symA = Symbol('A');
const symB = Symbol('B');
return Promise.allSettledKeyed({
a: Promise.resolve(1),
[symA]: Promise.resolve(2),
[symB]: Promise.resolve(3),
}).then(it => {
assert.deepEqual(it.a, { status: 'fulfilled', value: 1 }, 'string key');
assert.deepEqual(it[symA], { status: 'fulfilled', value: 2 }, 'symbol key A');
assert.deepEqual(it[symB], { status: 'fulfilled', value: 3 }, 'symbol key B');
});
});
QUnit.test('Promise.allSettledKeyed, keys order', assert => {
return Promise.allSettledKeyed({
a: Promise.resolve(1),
b: new Promise(resolve => setTimeout(() => resolve(2), 10)),
c: Promise.resolve(3),
}).then(it => {
const actualKeys = Object.keys(it);
assert.deepEqual(actualKeys, ['a', 'b', 'c'], 'correct order in the case when promises resolves in different order');
});
});
+176
View File
@@ -0,0 +1,176 @@
import { nullProto } from '../helpers/helpers.js';
import $allKeyed from 'core-js-pure/actual/promise/all-keyed';
import Promise from 'core-js-pure/actual/promise';
import create from 'core-js-pure/es/object/create';
import defineProperty from 'core-js-pure/es/object/define-property';
import getPrototypeOf from 'core-js-pure/es/object/get-prototype-of';
import keys from 'core-js-pure/es/object/keys';
import Symbol from 'core-js-pure/es/symbol';
QUnit.test('Promise.allKeyed', assert => {
assert.isFunction(Promise.allKeyed);
assert.arity(Promise.allKeyed, 1);
assert.true(Promise.allKeyed({}) instanceof Promise, 'returns a promise');
});
QUnit.test('Promise.allKeyed, resolved', assert => {
return Promise.allKeyed({
a: Promise.resolve(1),
b: Promise.resolve(2),
c: Promise.resolve(3),
}).then(it => {
assert.deepEqual(it, nullProto({
a: 1,
b: 2,
c: 3,
}), 'resolved with a correct value');
});
});
QUnit.test('Promise.allKeyed, resolved with primitives', assert => {
return Promise.allKeyed({
a: 1,
b: Promise.resolve(2),
c: 3,
}).then(it => {
assert.deepEqual(it, nullProto({
a: 1,
b: 2,
c: 3,
}), 'resolved with a correct value');
});
});
QUnit.test('Promise.allKeyed, resolved with rejection', assert => {
return Promise.allKeyed({
a: Promise.resolve(1),
b: Promise.reject(2),
c: Promise.resolve(3),
}).then(() => {
assert.avoid();
}, error => {
assert.same(error, 2, 'rejected with a correct value');
});
});
QUnit.test('Promise.allKeyed, resolved with empty object', assert => {
return Promise.allKeyed({}).then(it => {
assert.deepEqual(it, nullProto({}), 'resolved with a correct value');
});
});
QUnit.test('Promise.allKeyed, resolved with hidden attributes', assert => {
const obj = create({ proto: Promise.resolve('hidden') });
obj.visible = Promise.resolve(42);
defineProperty(obj, 'invisible', { value: Promise.resolve(99), enumerable: false });
return Promise.allKeyed(obj).then(it => {
assert.deepEqual(it, nullProto({ visible: 42 }), 'ignores prototype/invisible');
});
});
QUnit.test('Promise.allKeyed, rejected on incorrect input', assert => {
return Promise.allKeyed('string').then(() => {
assert.avoid();
}, error => {
assert.true(error instanceof TypeError, 'error is TypeError');
assert.required('rejected as expected');
});
});
QUnit.test('Promise.allKeyed, resolved with timeouts', assert => {
return Promise.allKeyed({
a: Promise.resolve(1),
b: new Promise(resolve => setTimeout(() => resolve(2), 10)),
c: Promise.resolve(3),
}).then(it => {
assert.deepEqual(it, nullProto({
a: 1,
b: 2,
c: 3,
}), 'keeps correct mapping, even with delays');
});
});
QUnit.test('Promise.allKeyed, subclassing', assert => {
const { allKeyed, resolve } = Promise;
function SubPromise(executor) {
executor(() => { /* empty */ }, () => { /* empty */ });
}
SubPromise.resolve = resolve.bind(Promise);
assert.true(allKeyed.call(SubPromise, { a: Promise.resolve(1) }) instanceof SubPromise, 'subclassing, `this` pattern');
function FakePromise1() { /* empty */ }
function FakePromise2(executor) {
executor(null, () => { /* empty */ });
}
function FakePromise3(executor) {
executor(() => { /* empty */ }, null);
}
FakePromise1.resolve = FakePromise2.resolve = FakePromise3.resolve = resolve.bind(Promise);
assert.throws(() => {
allKeyed.call(FakePromise1, { a: Promise.resolve(1) });
}, 'NewPromiseCapability validations, #1');
assert.throws(() => {
allKeyed.call(FakePromise2, { a: Promise.resolve(1) });
}, 'NewPromiseCapability validations, #2');
assert.throws(() => {
allKeyed.call(FakePromise3, { a: Promise.resolve(1) });
}, 'NewPromiseCapability validations, #3');
});
QUnit.test('Promise.allKeyed, without constructor context', assert => {
const { allKeyed } = Promise;
assert.throws(() => allKeyed({ a: Promise.resolve(1) }), TypeError, 'Throws if called without a constructor context');
assert.throws(() => allKeyed.call(null, { a: Promise.resolve(1) }), TypeError, 'Throws if called with null as this');
});
QUnit.test('Promise.allKeyed, method from direct entry, without constructor context', assert => {
return $allKeyed({ a: Promise.resolve(1) }).then(it => {
assert.deepEqual(it, nullProto({ a: 1 }), 'resolved with a correct value');
});
});
QUnit.test('Promise.allKeyed, method from direct entry, with null context', assert => {
return $allKeyed.call(null, { a: Promise.resolve(1) }).then(it => {
assert.deepEqual(it, nullProto({ a: 1 }), 'resolved with a correct value');
});
});
QUnit.test('Promise.allKeyed, result object has null prototype', assert => {
return Promise.allKeyed({
a: Promise.resolve(1),
b: Promise.resolve(2),
}).then(result => {
assert.strictEqual(
getPrototypeOf(result),
null,
'Result object has null prototype',
);
});
});
QUnit.test('Promise.allKeyed, symbol keys', assert => {
const symA = Symbol('A');
const symB = Symbol('B');
return Promise.allKeyed({
a: Promise.resolve(1),
[symA]: Promise.resolve(2),
[symB]: Promise.resolve(3),
}).then(it => {
assert.same(it.a, 1, 'string key');
assert.same(it[symA], 2, 'symbol key A');
assert.same(it[symB], 3, 'symbol key B');
});
});
QUnit.test('Promise.allKeyed, keys order', assert => {
return Promise.allKeyed({
a: Promise.resolve(1),
b: new Promise(resolve => setTimeout(() => resolve(2), 10)),
c: Promise.resolve(3),
}).then(it => {
const actualKeys = keys(it);
assert.deepEqual(actualKeys, ['a', 'b', 'c'], 'correct order in the case when promises resolves in different order');
});
});
@@ -0,0 +1,186 @@
import { nullProto } from '../helpers/helpers.js';
import $allSettledKeyed from 'core-js-pure/actual/promise/all-settled-keyed';
import Promise from 'core-js-pure/actual/promise';
import create from 'core-js-pure/es/object/create';
import defineProperty from 'core-js-pure/es/object/define-property';
import getPrototypeOf from 'core-js-pure/es/object/get-prototype-of';
import keys from 'core-js-pure/es/object/keys';
import Symbol from 'core-js-pure/es/symbol';
QUnit.test('Promise.allSettledKeyed', assert => {
const { allSettledKeyed } = Promise;
assert.isFunction(allSettledKeyed);
assert.arity(allSettledKeyed, 1);
});
QUnit.test('Promise.allSettledKeyed, resolved', assert => {
return Promise.allSettledKeyed({
a: Promise.resolve(1),
b: Promise.resolve(2),
c: Promise.resolve(3),
}).then(it => {
assert.deepEqual(it, nullProto({
a: { value: 1, status: 'fulfilled' },
b: { value: 2, status: 'fulfilled' },
c: { value: 3, status: 'fulfilled' },
}), 'resolved with a correct value');
});
});
QUnit.test('Promise.allSettledKeyed, resolved with primitives', assert => {
return Promise.allSettledKeyed({
a: 1,
b: Promise.resolve(2),
c: 3,
}).then(it => {
assert.deepEqual(it, nullProto({
a: { value: 1, status: 'fulfilled' },
b: { value: 2, status: 'fulfilled' },
c: { value: 3, status: 'fulfilled' },
}), 'resolved with a correct value');
});
});
QUnit.test('Promise.allSettledKeyed, resolved with rejection', assert => {
return Promise.allSettledKeyed({
a: Promise.resolve(1),
b: Promise.reject(2),
c: Promise.resolve(3),
}).then(it => {
assert.deepEqual(it, nullProto({
a: { value: 1, status: 'fulfilled' },
b: { reason: 2, status: 'rejected' },
c: { value: 3, status: 'fulfilled' },
}), 'resolved with a correct value');
});
});
QUnit.test('Promise.allSettledKeyed, rejected', assert => {
return Promise.allSettledKeyed().then(() => {
assert.avoid();
}, () => {
assert.required('rejected as expected');
});
});
QUnit.test('Promise.allSettledKeyed, resolved with empty object', assert => {
return Promise.allSettledKeyed({}).then(it => {
assert.deepEqual(it, nullProto({}), 'resolved with a correct value');
});
});
QUnit.test('Promise.allSettledKeyed, resolved with hidden attributes', assert => {
const obj = create({ proto: Promise.resolve('hidden') });
obj.visible = Promise.resolve(42);
defineProperty(obj, 'invisible', { value: Promise.resolve(99), enumerable: false });
return Promise.allSettledKeyed(obj).then(it => {
assert.deepEqual(it, nullProto({ visible: { status: 'fulfilled', value: 42 } }), 'ignores prototype/invisible');
});
});
QUnit.test('Promise.allSettledKeyed, rejected on incorrect input', assert => {
return Promise.allSettledKeyed('string').then(() => {
assert.avoid();
}, error => {
assert.true(error instanceof TypeError, 'error is TypeError');
assert.required('rejected as expected');
});
});
QUnit.test('Promise.allSettledKeyed, resolved with timeouts', assert => {
return Promise.allSettledKeyed({
a: Promise.resolve(1),
b: new Promise(resolve => setTimeout(() => resolve(2), 10)),
c: Promise.resolve(3),
}).then(it => {
assert.deepEqual(it, nullProto({
a: { value: 1, status: 'fulfilled' },
b: { value: 2, status: 'fulfilled' },
c: { value: 3, status: 'fulfilled' },
}), 'keeps correct mapping, even with delays');
});
});
QUnit.test('Promise.allSettledKeyed, subclassing', assert => {
const { allSettledKeyed, resolve } = Promise;
function SubPromise(executor) {
executor(() => { /* empty */ }, () => { /* empty */ });
}
SubPromise.resolve = resolve.bind(Promise);
assert.true(allSettledKeyed.call(SubPromise, { a: 1, b: 2, c: 3 }) instanceof SubPromise, 'subclassing, `this` pattern');
function FakePromise1() { /* empty */ }
function FakePromise2(executor) {
executor(null, () => { /* empty */ });
}
function FakePromise3(executor) {
executor(() => { /* empty */ }, null);
}
FakePromise1.resolve = FakePromise2.resolve = FakePromise3.resolve = resolve.bind(Promise);
assert.throws(() => {
allSettledKeyed.call(FakePromise1, { a: 1, b: 2, c: 3 });
}, 'NewPromiseCapability validations, #1');
assert.throws(() => {
allSettledKeyed.call(FakePromise2, { a: 1, b: 2, c: 3 });
}, 'NewPromiseCapability validations, #2');
assert.throws(() => {
allSettledKeyed.call(FakePromise3, { a: 1, b: 2, c: 3 });
}, 'NewPromiseCapability validations, #3');
});
QUnit.test('Promise.allSettledKeyed, without constructor context', assert => {
const { allSettledKeyed } = Promise;
assert.throws(() => allSettledKeyed({ a: Promise.resolve(1) }), TypeError, 'Throws if called without a constructor context');
assert.throws(() => allSettledKeyed.call(null, { a: Promise.resolve(1) }), TypeError, 'Throws if called with null as this');
});
QUnit.test('Promise.allSettledKeyed, method from direct entry, without constructor context', assert => {
return $allSettledKeyed({ a: Promise.resolve(1) }).then(it => {
assert.deepEqual(it, nullProto({ a: { status: 'fulfilled', value: 1 } }), 'resolved with a correct value');
});
});
QUnit.test('Promise.allSettledKeyed, method from direct entry, with null context', assert => {
return $allSettledKeyed.call(null, { a: Promise.resolve(1) }).then(it => {
assert.deepEqual(it, nullProto({ a: { status: 'fulfilled', value: 1 } }), 'resolved with a correct value');
});
});
QUnit.test('Promise.allSettledKeyed, result object has null prototype', assert => {
return Promise.allSettledKeyed({
a: Promise.resolve(1),
b: Promise.resolve(2),
}).then(result => {
assert.strictEqual(
getPrototypeOf(result),
null,
'Result object has null prototype',
);
});
});
QUnit.test('Promise.allSettledKeyed, symbol keys', assert => {
const symA = Symbol('A');
const symB = Symbol('B');
return Promise.allSettledKeyed({
a: Promise.resolve(1),
[symA]: Promise.resolve(2),
[symB]: Promise.resolve(3),
}).then(it => {
assert.deepEqual(it.a, { status: 'fulfilled', value: 1 }, 'string key');
assert.deepEqual(it[symA], { status: 'fulfilled', value: 2 }, 'symbol key A');
assert.deepEqual(it[symB], { status: 'fulfilled', value: 3 }, 'symbol key B');
});
});
QUnit.test('Promise.allSettledKeyed, keys order', assert => {
return Promise.allSettledKeyed({
a: Promise.resolve(1),
b: new Promise(resolve => setTimeout(() => resolve(2), 10)),
c: Promise.resolve(3),
}).then(it => {
const actualKeys = keys(it);
assert.deepEqual(actualKeys, ['a', 'b', 'c'], 'correct order in the case when promises resolves in different order');
});
});