前端手写Promise.all,你不知道的多个知识点,比想象中更精彩!

319 阅读5分钟

Promise.all 接受一个 Promise 可迭代对象作为输入,返回一个 Promise。当所有的输入都成功时,则返回一个包含所有成功结果的数组;如果任何一个输入失败时,则返回失败结果。

本文将通过知识问答、手写实现、知识扩展几个环节,让你彻底摸透promise.all。

知识问答

1. 什么情况下可以使用Promise.all?

  • 同时发起多个一步请求,等待所有的请求完成后再做出处理
  • 多个异步任务之间相互独立,但需要等待所有的异步任务完成后再做出处理

2.Promise.all迭代参数中如果传入的不是promise对象,返回的是什么?

Promise.all会将其隐式转换为Promise对象,并将其视为已完成状态

3.Promise.all与Promise.race有什么区别?

Promise.all等待所有的Promise完成,而Promise.race只等待第一个完成的Promise。如果Promise.race中的任何一个Promise完成或失败,它将立即返回该Promise的结果或错误

4.能否手动取消Promise.all中的某个Promise?

标准的Promise API 不提供取消功能。一旦Promise开始执行,就不能被取消

5.如果Promise.all中的一个promise永远不resolve也不reject,会发生什么?

如果Promise.all中的一个Promise永远不解析(即,它是一个悬而未决的状态),那么Promise.all将永远等待,直到所有其他的Promise都完成。这可能导致应用程序挂起或超时。在这种情况下,可能需要设置超时机制来处理这种情况

手写实现Promise.all

1. 定义返回值

Promise.all 返回的结果也是一个 Promise,先自定义一个叫myAll的方法,直接返回一个promise。

/* 自定义myAll函数 */
Promise.myAll = function () {
  const promise = new Promise();
  return promise;
}

写个例子,模拟调用我们自己写的myAll方法

/* 定义几个接受参数 */
const p1 = Promise.resolve(3);
const p2 = 1337;
const p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("foo");
    }, 100);
});

/* 调用myAll函数 */
Promise.myAll([p1, p2, p3]).then((res) => {
  console.log(res);
});

2. 对空参数的处理

我们常常使用Promise.all 时,传入一个空数组作为参数,此时会直接返回成功状态,返回结果也为一个空数组。

但这里不能简单的通过判断接受参数的长度来处理这种特殊情况,因为接受的参数是一个可迭代对象,可迭代对象不只有数组一种,还包含 String 、Array、TypedArray、Map、Set 以及 NodeList 等。

如何判断可迭代对象的长度?

使用 for...of语句,for...of语句执行一个循环,该循环处理来自可迭代对象的值序列。因为可迭代对象都必须含有 @@iterator 方法

Promise.myAll = function (args) {
    // 通过下面的promise,保存一份成功和失败的方法,用于后续的使用
    let res, rej;
    const promise = new Promise((resolve, reject) => {
        res = resolve;
        rej = reject;
    });

    // 记录接受对象的数量
    let count = 0;
    // 定义返回结果
    const result = [];
    for (const iterator of args) {
        count++;
    }

    // 如果没有可迭代内容,则直接返回成功
    if (count === 0) res(result);

    return promise;
};

3. 调用每个迭代对象

  • 任何一个迭代对象失败时,则返回失败结果
  • 全部迭代成功完成时,按照迭代顺序返回一个成功结果的数组
// 记录接受对象的数量
let count = 0;
// 记录完成的数量
let fulfilledCount = 0;
// 定义返回结果
const result = [];
for (const iterator of args) {
const index = count;
    count++;
	Promise.resolve(iterator).then((data) => {
        // 把返回结果
        result[index] = data;
        fulfilledCount++;
        // 完成数量等于总数量时,返回成功结果
        if (count === fulfilledCount) res(result);
    }, rej);
}

完成代码

Promise.myAll = function (args) {
    // 通过下面的promise,保存一份成功和失败的方法,用于后续的使用
    let res, rej;
    const promise = new Promise((resolve, reject) => {
        res = resolve;
        rej = reject;
    });

    // 记录接受对象的数量
    let count = 0;
    // 记录完成的数量
    let fulfilledCount = 0;
    // 定义返回结果
    const result = [];
    for (const iterator of args) {
        const index = count;
        count++;
        Promise.resolve(iterator).then((data) => {
            // 把返回结果
            result[index] = data;
            fulfilledCount++;
            // 完成数量等于总数量时,返回成功结果
            if (count === fulfilledCount) res(result);
        }, rej);
    }

    // 如果没有可迭代内容,则直接返回成功
    if (count === 0) res(result);

    return promise;
};

const p1 = Promise.resolve(3);
const p2 = 1337;
const p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("foo");
    }, 100);
});

Promise.myAll([p1, p2, p3]).then((res) => {
    console.log(res);
});

// [ 3, 1337, 'foo' ]

知识扩展

1. 非promise对象的隐式转换

const p1 = Promise.resolve(3);
const p2 = 1337;
const p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("foo");
    }, 100);
});
const p4 = undefined;
const p5 = {};
const p6 = null;
const p7 = true;

const promiseList = [p1, p2, p3, p4, p5, p6, p7];

Promise.all(promiseList).then((res) => {
    console.log(res);
});
// [ 3, 1337, 'foo', undefined, {}, null, true ]

2. 什么是可迭代对象

在JS中,可迭代对象是指那些实现了Symbol.iterator方法的对象。

这种类型的对象允许开发者自定义遍历的行为,从而使得不仅仅是内置的数据结构(如数组、字符串)可以迭代,自定义的对象也可以遍历。

  • 实现Symbol.iterator方法:为了使一个对象成为可迭代对象,它必须实现Symbol.iterator方法。这个方法返回一个迭代器,即具有next()方法的对象。
  • 使用for...of循环:可以使用for...of循环来遍历可迭代对象。在每次迭代中,for...of会调用迭代器的next()方法,获取下一个值。
  • 自定义迭代行为:通过定义Symbol.iterator方法,开发者可以自定义迭代的行为,例如决定如何生成序列中的下一个值。
  • 内置的可迭代对象:除了自定义对象,JavaScript中的许多内置对象如数组、字符串、Map、Set等都是可迭代的。

写在最后

欢迎大家访问我的个人网站:www.dengzhanyong.com

关注我的个人公众号【前端筱园】,不错过每一篇推送

加入【前端筱园交流群】,与大家一起交流,共同进步!