Promise 手撕

promise 手撕

手动实现一个完整的 Promise 是一个复杂的任务,包括 Promise 的状态管理、异步任务调度、回调处理等。以下是一个简化版的 Promise 实现,用于演示 Promise 的基本原理:

promise

js 复制代码
class MyPromise {
  constructor(executor) {
    // 初始化 Promise 的状态为 pending
    this.state = 'pending';
    // 初始化 Promise 的结果值为 undefined
    this.value = undefined;
    // 存储 then 方法中的回调函数
    this.handlers = [];

    // 定义 resolve 方法,用于将 Promise 状态从 pending 变为 fulfilled
    const resolve = (value) => {
      // 确保 Promise 的状态只能从 pending 变为 fulfilled
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        // 存储 resolved 的结果值
        this.value = value;
        // 执行所有的回调函数
        this.callHandlers();
      }
    };

    // 定义 reject 方法,用于将 Promise 状态从 pending 变为 rejected
    const reject = (reason) => {
      // 确保 Promise 的状态只能从 pending 变为 rejected
      if (this.state === 'pending') {
        this.state = 'rejected';
        // 存储 rejected 的原因
        this.value = reason;
        // 执行所有的回调函数
        this.callHandlers();
      }
    };

    try {
      // 执行传入的 executor 函数,并将 resolve 和 reject 作为参数传递进去
      executor(resolve, reject);
    } catch (error) {
      // 如果执行过程中出现异常,直接将 Promise 状态置为 rejected
      reject(error);
    }
  }

  // 定义 then 方法,用于添加成功和失败的回调函数
  then(onFulfilled, onRejected) {
    // 返回一个新的 Promise 对象
    return new MyPromise((resolve, reject) => {
      // 定义 fulfilledHandler 函数,用于处理 fulfilled 状态的回调
      const fulfilledHandler = () => {
        if (typeof onFulfilled === 'function') {
          try {
            // 执行 onFulfilled 回调,并将 resolved 的结果传递给它
            const result = onFulfilled(this.value);
            // 将 onFulfilled 回调的结果传递给新的 Promise,从而实现链式调用
            resolve(result);
          } catch (error) {
            // 如果 onFulfilled 回调执行过程中出现异常,将新的 Promise 置为 rejected
            reject(error);
          }
        } else {
          // 如果 onFulfilled 不是函数,直接将 resolved 的结果传递给新的 Promise
          resolve(this.value);
        }
      };

      // 定义 rejectedHandler 函数,用于处理 rejected 状态的回调
      const rejectedHandler = () => {
        if (typeof onRejected === 'function') {
          try {
            // 执行 onRejected 回调,并将 rejected 的原因传递给它
            const result = onRejected(this.value);
            // 将 onRejected 回调的结果传递给新的 Promise,从而实现链式调用
            resolve(result);
          } catch (error) {
            // 如果 onRejected 回调执行过程中出现异常,将新的 Promise 置为 rejected
            reject(error);
          }
        } else {
          // 如果 onRejected 不是函数,直接将 rejected 的原因传递给新的 Promise
          reject(this.value);
        }
      };

      // 如果 Promise 的状态还是 pending,说明回调函数需要等待,将回调函数存储起来
      if (this.state === 'pending') {
        this.handlers.push({ fulfilledHandler, rejectedHandler });
      } else if (this.state === 'fulfilled') {
        // 如果 Promise 已经是 fulfilled 状态,立即执行 fulfilledHandler 回调
        fulfilledHandler();
      } else if (this.state === 'rejected') {
        // 如果 Promise 已经是 rejected 状态,立即执行 rejectedHandler 回调
        rejectedHandler();
      }
    });
  }

  // 定义 catch 方法,用于添加失败的回调函数,实际上就是 then 方法的语法糖
  catch(onRejected) {
    return this.then(null, onRejected);
  }

  // 执行所有的回调函数,保证 then 方法注册的回调能被调用
  callHandlers() {
    this.handlers.forEach(({ fulfilledHandler, rejectedHandler }) => {
      if (this.state === 'fulfilled') {
        // 如果 Promise 的状态是 fulfilled,执行 fulfilledHandler 回调
        fulfilledHandler();
      } else if (this.state === 'rejected') {
        // 如果 Promise 的状态是 rejected,执行 rejectedHandler 回调
        rejectedHandler();
      }
    });
  }

  // 定义静态方法 resolve,返回一个 fulfilled 的 Promise 对象
  static resolve(value) {
    return new MyPromise((resolve) => {
      resolve(value);
    });
  }

  // 定义静态方法 reject,返回一个 rejected 的 Promise 对象
  static reject(reason) {
    return new MyPromise((_, reject) => {
      reject(reason);
    });
  }

  // 定义静态方法 all,接收一个 Promise 数组作为参数,返回一个新的 Promise 对象
  static all(promises) {
  // 定义静态方法 all,接收一个 Promise 数组作为参数,返回一个新的 Promise 对象
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      const results = [];
      let resolvedCount = 0;

      promises.forEach((promise, index) => {
        // 将每个 Promise 转换为 MyPromise,确保所有 Promise 都被处理
        MyPromise.resolve(promise).then(
          value => {
            // 存储 resolved 的结果值
            results[index] = value;
            resolvedCount++;

            // 当所有 Promise 都被 resolved 时,将新的 Promise 置为 fulfilled,并传递结果数组
            if (resolvedCount === promises.length) {
              resolve(results);
            }
          },
          // 当有任何一个 Promise 被 rejected 时,将新的 Promise 置为 rejected,并传递原因
          reject
        );
      });
    });
  }

  // 定义静态方法 race,接收一个 Promise 数组作为参数,返回一个新的 Promise 对象
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      promises.forEach(promise => {
        // 将每个 Promise 转换为 MyPromise,确保所有 Promise 都被处理
        MyPromise.resolve(promise).then(
          // 当有任何一个 Promise 被 resolved 时,将新的 Promise 置为 fulfilled,并传递结果
          resolve,
          // 当有任何一个 Promise 被 rejected 时,将新的 Promise 置为 rejected,并传递原因
          reject
        );
      });
    });
  }
}

// 测试代码
const promise1 = new MyPromise(resolve => setTimeout(() => resolve(1), 1000));
const promise2 = new MyPromise(resolve => setTimeout(() => resolve(2), 500));
const promise3 = new MyPromise((resolve, reject) => setTimeout(() => reject('error'), 800));

MyPromise.all([promise1, promise2, promise3])
  .then(results => {
    console.log(results); // 输出:[1, 2, "error"]
  })
  .catch(error => {
    console.error(error);
  });

MyPromise.race([promise1, promise2, promise3])
  .then(result => {
    console.log(result); // 输出:2
  })
  .catch(error => {
    console.error(error);
  });

Promise.allSettled 是一个 ECMAScript 2020 中引入的新方法,它返回一个在所有给定的 Promise 都已经 fulfilled 或 rejected 后的 Promise,并带有一个对象数组,每个对象表示对应的 Promise 结果。如果一个 Promise 成功了,你会得到一个对象 { status: 'fulfilled', value: resolvedValue },如果一个 Promise 失败了,你会得到一个对象 { status: 'rejected', reason: rejectionReason }

Promise.allSettled

js

js 复制代码
function promiseAllSettled(promises) {
  return new Promise(resolve => {
    const results = [];
    let settledCount = 0;
promises.forEach((promise, index) => {
  Promise.resolve(promise)
    .then(value => {
      results[index] = { status: 'fulfilled', value };
      settledCount++;
      if (settledCount === promises.length) {
        resolve(results);
      }
    })
    .catch(reason => {
      results[index] = { status: 'rejected', reason };
      settledCount++;
      if (settledCount === promises.length) {
        resolve(results);
      }
    });
});
});
}

//输出
const promises = [
  Promise.resolve(1),
  Promise.reject('error'),
  Promise.resolve(3)
];

promiseAllSettled(promises)
  .then(results => {
    console.log(results);
    // 输出:[
    //   { status: 'fulfilled', value: 1 },
    //   { status: 'rejected', reason: 'error' },
    //   { status: 'fulfilled', value: 3 }
    // ]
  });

Promise.any 是 ECMAScript 2021 中引入的新方法,它返回一个在任意给定的 Promise 变为 fulfilled 状态后的 Promise。如果所有 Promise 都处于 rejected 状态,则会返回一个 AggregateError,其中包含所有的 rejection 原因。

Promise.any

js 复制代码
function promiseAny(promises) {
  return new Promise((resolve, reject) => {
    let rejectedCount = 0;
    const errors = [];
promises.forEach((promise, index) => {
  Promise.resolve(promise)
    .then(resolve)
    .catch(reason => {
      rejectedCount++;
      errors[index] = reason;
      if (rejectedCount === promises.length) {
        reject(new AggregateError(errors, 'All promises were rejected'));
      }
    });
});
  });
}

//使用上述实现的 promiseAny 函数,你可以按照以下方式调用:

const promises = [
  Promise.reject('Error 1'),
  Promise.resolve(2),
  Promise.reject('Error 3')
];

promiseAny(promises)
  .then(result => {
    console.log(result); // 输出:2
  })
  .catch(error => {
    console.error(error); // 输出:AggregateError: All promises were rejected
  });

Promise.all 是一个非常常用的 Promise 方法,它接收一个 Promise 数组作为输入,返回一个新的 Promise,该 Promise 在所有输入的 Promise 都变为 fulfilled 状态后才会变为 fulfilled 状态,并返回一个包含所有 Promise 结果的数组。如果输入的 Promise 中有一个被 rejected,则返回的 Promise 会立即变为 rejected,并带有第一个被 rejected 的 Promise 的原因。

Promise.all

js 复制代码
function promiseAll(promises) {
  return new Promise((resolve, reject) => {
    const results = [];
    let fulfilledCount = 0;
promises.forEach((promise, index) => {
  Promise.resolve(promise)
    .then(value => {
      results[index] = value;
      fulfilledCount++;
      if (fulfilledCount === promises.length) {
        resolve(results);
      }
    })
    .catch(reject);
});
  });
}
//使用上述实现的 promiseAll 函数,你可以按照以下方式调用:
const promises = [
  Promise.resolve(1),
  Promise.resolve(2),
  Promise.resolve(3)
];

promiseAll(promises)
  .then(results => {
    console.log(results); // 输出:[1, 2, 3]
  })
  .catch(error => {
    console.error(error);
  });

Promise.race 是一个 Promise 方法,它接收一个 Promise 数组作为输入,返回一个新的 Promise,该 Promise 在输入的 Promise 数组中有任意一个 Promise 变为 fulfilled 或 rejected 状态后,即会变为相应的状态,并带有该 Promise 的结果或原因。

Promise.race

js 复制代码
function promiseRace(promises) {
  return new Promise((resolve, reject) => {
    promises.forEach(promise => {
      Promise.resolve(promise)
        .then(resolve)
        .catch(reject);
    });
  });
}
//使用上述实现的 promiseRace 函数,你可以按照以下方式调用:
const promises = [
  new Promise(resolve => setTimeout(() => resolve(1), 1000)),
  new Promise(resolve => setTimeout(() => resolve(2), 500)),
  new Promise((resolve, reject) => setTimeout(() => reject('error'), 800))
];

promiseRace(promises)
  .then(result => {
    console.log(result); // 输出:2
  })
  .catch(error => {
    console.error(error); // 输出:error
  });

Promise.any 是 ECMAScript 2021 中引入的新方法,它返回一个在任意给定的 Promise 变为 fulfilled 状态后的 Promise。如果所有 Promise 都处于 rejected 状态,则会返回一个 AggregateError,其中包含所有的 rejection 原因。

Promise.any

js 复制代码
function promiseAny(promises) {
  return new Promise((resolve, reject) => {
    let rejectedCount = 0;
    const errors = [];

    promises.forEach((promise, index) => {
      Promise.resolve(promise)
        .then(resolve)
        .catch(reason => {
          rejectedCount++;
          errors[index] = reason;
          if (rejectedCount === promises.length) {
            reject(new AggregateError(errors, 'All promises were rejected'));
          }
        });
    });
  });
}
//使用上述实现的 promiseAny 函数,你可以按照以下方式调用:
const promises = [
  Promise.reject('Error 1'),
  Promise.resolve(2),
  Promise.reject('Error 3')
];

promiseAny(promises)
  .then(result => {
    console.log(result); // 输出:2
  })
  .catch(error => {
    console.error(error); // 输出:AggregateError: All promises were rejected
  });

并发实现

js 复制代码
function concurrentPromises(promises, concurrency) {
  return new Promise((resolve, reject) => {
    const results = [];
    let runningCount = 0;
    let index = 0;

    // 定义一个递归函数,用于执行下一个 Promise
    function runNext() {
      // 如果已经执行了所有的 Promise,则返回结果
      if (index >= promises.length) {
        resolve(results);
        return;
      }

      // 取出当前要执行的 Promise
      const promise = promises[index];

      // 执行当前 Promise,并更新状态
      runningCount++;
      index++;

      Promise.resolve(promise)
        .then(result => {
          // 当 Promise 执行成功时,将结果存入 results 数组
          results.push(result);
        })
        .catch(error => {
          // 当 Promise 执行失败时,直接返回错误
          reject(error);
        })
        .finally(() => {
          // 当前 Promise 执行完毕,继续执行下一个 Promise
          runningCount--;

          // 如果正在执行的 Promise 数量小于并发数,并且还有未执行的 Promise,则继续执行下一个 Promise
          while (runningCount < concurrency && index < promises.length) {
            runNext();
          }

          // 如果正在执行的 Promise 数量为 0,并且所有 Promise 都执行完毕,则返回结果
          if (runningCount === 0 && index >= promises.length) {
            resolve(results);
          }
        });
    }

    // 启动并发执行
    for (let i = 0; i < concurrency && i < promises.length; i++) {
      runNext();
    }
  });
}

// 测试代码
const delay = (ms, value) => new Promise(resolve => setTimeout(() => resolve(value), ms));

const promises = [
  delay(1000, 1),
  delay(2000, 2),
  delay(1500, 3),
  delay(500, 4),
  delay(800, 5),
  delay(1200, 6),
];

concurrentPromises(promises, 3)
  .then(results => {
    console.log(results); // 输出:[1, 2, 3, 4, 5, 6]
  })
  .catch(error => {
    console.error(error);
  });

上述代码中的 concurrentPromises 函数接受一个 Promise 数组 promises 和并发数 concurrency 作为参数。它会并发执行最多 concurrency 个 Promise,然后等待其中一个 Promise 完成后再继续执行下一个 Promise,直到所有 Promise 都执行完毕。

请注意,实际的 Promise 并发实现可能更加复杂,需要考虑更多的错误处理和边界情况。此处提供的是一个简单的示例实现,用于演示并发的基本原理。在实际项目中,建议使用成熟的 Promise 并发库,如 Promise.allPromise.allSettled,来实现并发任务。

总结

Promise的并发性方法常用于处理多个promise的并发执行问题,每个方法应用场景大致总结如下:

  • Promise.all:用于接收多个promise兑现的结果,若其中有reject的会被reject
  • Promise.allSettled:用于接收多个promise兑现/拒绝的结果
  • Promise.any:用于接收多个promise中兑现最快的那个的兑现结果,若全部reject则reject
  • Promise.race:用于接收多个promise中兑现/拒绝最快的那个的处理结果
相关推荐
热爱编程的小曾16 分钟前
sqli-labs靶场 less 8
前端·数据库·less
gongzemin28 分钟前
React 和 Vue3 在事件传递的区别
前端·vue.js·react.js
Apifox41 分钟前
如何在 Apifox 中通过 Runner 运行包含云端数据库连接配置的测试场景
前端·后端·ci/cd
树上有只程序猿1 小时前
后端思维之高并发处理方案
前端
庸俗今天不摸鱼2 小时前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
黄毛火烧雪下2 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox2 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞2 小时前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行2 小时前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_593758102 小时前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox