4

实现 Promise.all 有哪些要点

 2 years ago
source link: https://icodex.me/promiseall/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

实现 Promise.all 有哪些要点

2022年3月23日 · One min read
Front End Engineer

Promise.all

Promise.all是日常使用频率非常高的异步方法,然后面试的事后还老是问,要不然我才懒得看呢😄.不过这两天回顾了一下 Promise的几个静态方法,还真对Promise.all有了一些新的认识。

Promise.all 的定义

Promise.all(iterable)

理解Promise.allAPI 需要注意以下几点:

  1. 接收一个可迭代对象作为参数,否则throw TypeError的错误;
  2. 当迭代器内部所有Promise对象fulfilled的时候,Promise.all返回fulfilled状态的Promise对象,resolve参数为一个数组,包含所有非Promise对象以及所有Promise对象resolve的值;
  3. 当迭代器内部存在一个Promise对象状态变为rejected或者非Promise元素执行出错时,Promise.all将立即返回rejected状态的Promise对象,reject参数为rejectedPromise对象抛出的信息,或者非Promise元素执行报错的信息;
  4. Promise.all变成fulfilled状态时,resolve的数组元素按照迭代器元素的顺序,非Promise对象会直接放在对应位置返回。

Promise.all API 定义来看,首先需要理解什么是可迭代对象。

什么是可迭代

可迭代是 ES6 提出的语法协议,是指满足以下两个要求的对象:

  1. iterable:可迭代。可迭代指的是一个对象内部需要实现Symbol.iterator方法,该方法不接收参数,同时返回一个对象,这个对象必须是迭代器。当使用for...of遍历的时候,会调用Symbol.iterator方法。

  2. iterator:迭代器。迭代器是一个对象,包含一个next方法,该方法可接收一个参数,同时返回一个包含以下两个属性的对象。

  • done: boolean:表示当前迭代器是否迭代完毕,结束迭代则设为true,此时value可以省略,因为没东西要输出了嘛。
  • value:any:迭代器每次调用返回的值

基于class实现的可迭代对象示例如下:

class SimpleClass {
constructor(data) {
this.data = data
}

// 首先实现 Symbol.iterator 方法
[Symbol.iterator]() {
let index = 0;

// 返回一个迭代器
return {
// 迭代器内部必须包含 next 方法
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false }
} else {
return { done: true }
}
}
}
}
}

目前内置的可迭代对象有以下这些:

Array
String
Map
Set
TypedArray

这些类型的值都可以使用for...of遍历。

caution

有个需要注意的点是,无法准确判断一个对象是否可迭代,因为要同时满足上述说的两个条件有个 hack 的方法。

const myIterator = {
next: function() {
// ...
},
[Symbol.iterator]: function() { return this; }
};

但是对于日常来说,谁会写这么 hack 的代码呢?所以要判断一个对象是否可迭代仍然可以使用:

typeof sth[Symbol.iterator] === 'function'

Promise.all 的实现

Promise._all = function (iterable) {
// 判断迭代器
if (typeof iterable?.[Symbol.iterator] !== "function") {
throw new TypeError("parameter must be iterable");
}

return new Promise((resolve, reject) => {
let count = [...iterable].length;
// 如果是空的迭代器,则直接 resolve 空数组
if (count === 0) {
resolve([]);
} else {
const resultArr = [];
for (let it of iterable) {
Promise.resolve(it)
.then((v) => {
count--;
resultArr.push(v);
if (count === 0) {
resolve(resultArr);
}
})
.catch((err) => {
reject(err);
});
}
}
});
};
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});

Promise._all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// [ 3, 42, 'foo' ]

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK