Array.fromAsync 方法是 ECMAScript 的一个提案,最近已经进入了 Stage-3 阶段,很可能在明年纳入到 ECMAScript 2023 标准中,本文对 Array.fromAsync 做一个简单介绍。
在 ECMAScript 6 标准中,引入了 Array.from 方法,可以将对一个类似数组或可迭代对象创建一个新的、浅拷贝的数组实例。比如:
// Using an arrow function as the map function to
// manipulate the elements
Array.from([1, 2, 3], x => x + x);
// [2, 4, 6]
// Generate a sequence of numbers
// Since the array is initialized with `undefined` on each position,
// the value of `v` below will be `undefined`
Array.from({length: 5}, (v, i) => i);
// [0, 1, 2, 3, 4]
Array.from 可以看作是一个遍历伪数组对象或可迭代对象并转换成数组的方法:
const arr = [];
for (const v of iterable) {
arr.push(v);
}
// This does the same thing.
const arr = Array.from(iterable);
但是对于异步迭代对象,还没有类似于 Array.from 的方法,这样的方法对于将整个异步迭代器转储到单个数据结构中也很有用,尤其是在单元测试或命令行场景中:
const arr = [];
for await (const v of asyncIterable) {
arr.push(v);
}
// We should add something that does the same thing.
const arr = await ??????????(asyncIterable);
正如 Array.from 用于 for 的场景,Array.fromAsync 则是用于 for await 的场景。
与 Array.from 一样,Array.fromAsync 也有三个参数:items, mapfn, thisArg,其中 items 是必填参数,mapfn, thisArg 是选填参数。items 既可以是异步迭代器,也可以是同步迭代器,或者是 array-like 对象。
异步迭代器
Array.fromAsync 可以将一个异步迭代器转化成为一个 promise,并且 resolve 出一个新的数组。在 promise resolve 之前,会根据输入的参数创建一个异步迭代器,然后惰性的迭代它,并且把每次 yield 的返回值添加到数组中。
async function * asyncGen (n) {
for (let i = 0; i < n; i++)
yield i * 2;
}
// `arr` will be `[0, 2, 4, 6]`.
const arr = [];
for await (const v of asyncGen(4)) {
arr.push(v);
}
// This is equivalent.
const arr = await Array.fromAsync(asyncGen(4));
同步迭代器
如果输入的参数是一个同步迭代器,那么 Array.fromAsync 的返回值仍然是一个 promise,然后 resolve 出一个新的数组。如果同步迭代器 yield 的返回值是 promise,那么将 await 每个 promise 的处理结果,并将这些结果添加到新数组中。
function * genPromises (n) {
for (let i = 0; i < n; i++)
yield Promise.resolve(i * 2);
}
// `arr` will be `[ 0, 2, 4, 6 ]`.
const arr = [];
for await (const v of genPromises(4)) {
arr.push(v);
}
// This is equivalent.
const arr = await Array.fromAsync(genPromises(4));
array-like 对象
对于那些有 length 属性和索引属性的 array-like 对象,Array.fromAsync 也会返回一个新数组:
const arrLike = {
length: 4,
0: Promise.resolve(0),
1: Promise.resolve(2),
2: Promise.resolve(4),
3: Promise.resolve(6),
}
// `arr` will be `[ 0, 2, 4, 6 ]`.
const arr = [];
for await (const v of Array.from(arrLike)) {
arr.push(v);
}
// This is equivalent.
const arr = await Array.fromAsync(arrLike);
在下面的测试用例中,需要将 Pipeline 中的数据放到一个数组中,然后完成用例的断言:
async function toArray(items) {
const result = [];
for await (const item of items) {
result.push(item);
}
return result;
}
it('empty-pipeline', async () => {
const pipeline = new Pipeline();
const result = await toArray(
pipeline.execute(
[ 1, 2, 3, 4, 5 ]));
assert.deepStrictEqual(
result,
[ 1, 2, 3, 4, 5 ],
);
});
使用了 Array.fromAsync 可以简化为:
it('empty-pipeline', async () => {
const pipeline = new Pipeline();
const result = await Array.fromAsync(
pipeline.execute(
[ 1, 2, 3, 4, 5 ]));
assert.deepStrictEqual(
result,
[ 1, 2, 3, 4, 5 ],
);
});
目前,Array.fromAsync 处于提案阶段,还并未正式成为标准。如果想使用它,需要使用 polyfill 支持,下面是它的示例代码:
async function fromAsync (items, mapfn, thisArg) {
const itemsAreIterable = (
asyncIteratorSymbol in items ||
iteratorSymbol in items
);
if (itemsAreIterable) {
const result = isConstructor(this)
? new this
: IntrinsicArray(0);
let i = 0;
for await (const v of items) {
if (i > MAX_SAFE_INTEGER) {
throw TypeError(tooLongErrorMessage);
}
else if (mapfn) {
result[i] = await mapfn.call(thisArg, v, i);
}
else {
result[i] = v;
}
i ++;
}
result.length = i;
return result;
}
else {
// In this case, the items are assumed to be an arraylike object with
// a length property and integer properties for each element.
const { length } = items;
const result = isConstructor(this)
? new this(length)
: IntrinsicArray(length);
let i = 0;
while (i < length) {
if (i > MAX_SAFE_INTEGER) {
throw TypeError(tooLongErrorMessage);
}
const v = await items[i];
if (mapfn) {
result[i] = await mapfn.call(thisArg, v, i);
}
else {
result[i] = v;
}
i ++;
}
result.length = i;
return result;
}
};
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8