寿司🍣 Promise 之 API (all, allSettled, any, race, resolve, reject)

寿司🍣 Promise 之 API (all, allSettled, any, race, resolve, reject)

分析异同点

首先,我们对这些 API 进行分析,提取他们的共同之处,和发现他们的不同之处。

共同点

  1. 参数都接受一个 promise 类型的iterable类型的输入(注:Array,Map,Set都属于ES6的inerable类型)
  2. 这些方法都返回一个Promise实例

不同点

  1. 返回的Promise实例的状态改变时机不同

    • Promise.all:在所有输入的 Promise 实例都resolve后执行自身的resolve回调,在任意一个输入的Promise实例reject后执行自身的reject回调
    • Promise.allSettled:在所有输入的 Promise 实例改变状态(resolve或者reject)后执行自身的resolve回调
    • Promise.any:在所有输入的 Promise 实例都reject后执行自身的reject回调,在任意一个输入的Promise实例resolve后执行自身的resolve回调
    • Promise.race:在任意一个输入的Promise实例改变状态后以相同的状态改变自身
  2. 返回的Promise实例的终值(eventual value)或拒因(reason)不同

    • Promise.all方法返回的Promise实例终值是一个数组,数组的成员是所有输入的 Promise 实例的终值,并按照参数内的 promise 顺序排列,而不是由调用 promise 的完成顺序决定。拒因是输入的 Promise 实例中第一个状态变为reject的拒因

    • Promise.allSettled方法返回的 Promise 实例终值也是一个数组,并且按照参数内的promise顺序排列。其中的每个成员在输入promise为resolved状态时为{status:'fulfilled', value:同一个终值}rejected状态时为{status:'rejected', reason:同一个拒因}

      1
      2
      3
      4
      5
      6
      7
      8
      const promise1 = Promise.resolve(3);
      const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
      const promises = [promise1, promise2];

      Promise.allSettled(promises).
      then((results) => results.forEach((result) => console.log(result)));
      // Object { status: "fulfilled", value: 3 }
      // Object { status: "rejected", reason: "foo" }
  3. 参数为空迭代对象时,返回值不同

    • Promise.all同步地返回一个已完成(resolved)状态的promise,其终值为空数组。(而且Promise.all 当且仅当传入的可迭代对象为空时是同步
    • Promise.allSettled:与Promise.all表现相同
    • Promise.any同步地返回一个已失败(rejected)状态的 promise,其拒因是一个 AggregateError 对象。
    • Promise.race:返回一个永远等待的promise

用同一个思路处理这些 API

根据上文的异同点分析,我们需要对参数进行判断是否为iterable对象。定义一个结果收集数组和一个表示符合条件的 promise 状态个数变量,并且返回一个Promise实例。

通用模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function template(promises) {
if (promises.length === 0) {
// 根据不同情况作处理
}
let result = [],
num = 0;
return new Promise((resolve, reject) => {
const check = () => {
if (num === promises.length) {
// 根据不同情况调用 resolve 或 reject
}
};
promises.forEach((item) => {
Promise.resolve(item).then(
(res) => {
// 根据不同情况处理 result、num 和调用 resolve、reject、check 方法
},
(err) => {
// 根据不同情况处理 result、num 和调用 resolve、reject、check 方法
}
);
});
});
}

插播一下 Promise.resolve 这个函数:

Promise.resolve(value)方法返回一个以给定值解析后的 Promise 对象。如果这个值是一个 promise ,那么将返回这个 promise ;如果这个值是 thenable(即带有”then” 方法),返回的 promise 会“跟随”这个 thenable 的对象,采用它的最终状态;否则返回的 promise 将以此值完成。

因为 promises 的成员里可能混入了一些不是 promise 的值,所以用 Promise.resolve 去解析后就能统一为其添加 then 回调了。

Promise.all

Promise.all:

  • 传入的所有 Promsie 都是 fulfilled,则返回由他们的值组成的,状态为 fulfilled 的新 Promise
  • 只要有一个 Promise 是 rejected,则返回 rejected 状态的新 Promsie,且它的值是第一个 rejected 的 Promise 的值
  • 只要有一个 Promise 是 pending,则返回一个 pending 状态的新 Promise
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function all(promises) {
if (promises.length === 0) {
return Promise.resolve([]);
}

let result = [],
num = 0;

return new Promise((resolve, reject) => {
const check = () => {
if (num === promises.length) {
resolve(result);
}
};

promises.forEach((item, index) => {
Promise.resolve(item).then(
(res) => {
num++;
result[index] = res;
check();
},
(err) => {
reject(err);
}
);
});
});
}

Promise.allSettled

Promise.allSettled:

  • 所有 Promise 的状态都变化了,那么新返回一个状态是 fulfilled 的 Promise,且它的值是一个数组,数组的每项由所有 Promise 的值和状态组成的对象
  • 如果有一个是 pending 的 Promise,则返回一个状态是 pending 的新实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function allSettled(promises) {
if (promises.length === 0) {
return Promise.resolve([]);
}

let result = [],
sum = 0;

return new Promise((resolve, reject) => {
const check = () => {
if (num === promises.length) {
resolve(result);
}
};

promises.forEach((item, index) => {
Promise.resolve(item).then(
(res) => {
result[index] = { status: "fulfilled", value: res };
num++;
check();
},
(err) => {
result[index] = { status: "rejected", reason: err };
num++;
check();
}
);
});
});
}

Promise.any

Promise.any:

  • 空数组或者所有 Promise 都是 rejected,则返回状态是 rejected 的新 Promsie,且值为 AggregateError 的错误
  • 只要有一个是 fulfilled 状态的,则返回第一个是 fulfilled 的新实例
  • 其他情况都会返回一个 pending 的新实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function any(promises) {
if (promises.length === 0) {
return Promise.reject(
new AggregateError("No Promise in Promise.any was resolved")
);
}

let result = [],
num = 0;

return new Promise((resolve, reject) => {
const check = () => {
if (num === result.length) {
reject(new AggregateError("No Promise in Promise.any was resolved"));
}
};

promises.forEach((item, index) => {
Promise.resolve(item).then(
(res) => {
resolve(res);
},
(err) => {
result[index] = err;
num++;
check();
}
);
});
});
}

Promise.race

Promise.race:返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function race(promises) {
if (!promises.length) {
throw Error("Promise.race need length");
}

return new Promise((resolve, reject) => {
promises.forEach((item) => {
Promise.resolve(item).then(
(res) => {
resolve(res);
},
(err) => {
reject(err);
}
);
});
});
}

Promise.resolve 和 Promise.reject

Promsie.resolve(value) 可以将任何值转成值为 value ,状态是 fulfilled 的 Promise,但如果传入的值本身是 Promise 则会原样返回它。

1
2
3
4
5
6
7
8
9
10
11
12
13
// Promise.resolve
Promise.resolve = function(value) {
// 传入的值是 Promise
if(value instanceof Promise) {
return value
}

return new Promise(resolve => resolve(value))
}

// 如果传入的值本身是Promise则会原样返回他
let p = new Promise((resolve) => resolve(3));
console.log(p === Promise.resolve(p)); // true

Promise.reject() 会实例化一个 rejected 状态的 Promise。但与 Promise.resolve() 不同的是,如果给 Promise.reject() 传递一个 Promise 对象,则这个对象会成为新 Promise 的值。

1
2
3
4
5
6
7
8
9
10
// Promise.reject
Promise.reject = function(reason) {
return new Promise((resolve, reject) =>{
reject(reason)
})
}

// 与 Promise.resolve() 不同的是,如果给 Promise.reject() 传递一个 Promise 对象,则这个对象会成为新 Promise 的值
let p = new Promise((resolve,reject) => reject(3));
console.log(p === Promise.reject(p)); // false

参考资料

同一个套路手撕 Promise 的 all、allSettled、any、race 方法

Promise.all()

Promise.allSettled()

Promise.any()

Promise.race

Promise.resolve()

死磕 36 个 JS 手写题(搞懂后,提升真的大)

深入理解Promise