如何手写一个Promise

经常听到有说要考验前端开发者的能力,让他手写一个promise,手写一个promise到底是什么意思?如果从编程解题,或编程技术 上看,“手写一个promise” 的使用什么编程技术 ,编程任务又是什么?

要回答这些问题,得先明白 promise 是什么。

promise对象 VS Promise API

首先明白,promise 这个字表达是一种概念,思想;在技术,有promise对象,和promise API 的不同。例如 JS 有一个 Promise类,也有返回 promise对象的API,例如 fetch。

promise是设计用来改善 原异步编程方式的,有一定前端开发经验的人一定明白 异步回调(callback),像定时器API:

setTimeout(() => saySomething("10 seconds passed"), 10 * 1000);

其实 promise对象 也是一种异步回调编程(工具),promise 背后也基于 callback 机制 ,只是对 原始 callback注1 进行逻辑封装,目的是为让「多步回调任务」流程更清晰,方便维护和异常处理。

手写promise的真正任务

从以上分析可看,“手写一个promise” 可以两层意思:第一,利用一个包装好的promise对象 进行异步业务编程;第二,是对原始的异步 callback进行包装,实现某种promise API。两个任务的性质完全 不一样,前者是一种业务编程,后者是一种功能(更底层)编程,要开发一个特别的类构函数(JS 已经提供了这个 Promise类)。

“手写一个promise” 指的应该是后一种编程任务,要求你 重写系统的Promise类。无论是哪一种编程,了解 promise异步业务逻辑 都是它们的基础。

其实还有第三种,就是用 系统的Promise 类 API(因为 Promise类只是个模板 )对一个原始异步操作进行包装,例如如下对 定时器 包装一个更具语义的 wait 异步函数:

const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

wait(10 * 1000)
 .then(() => saySomething("10 seconds"))
 .catch(failureCallback);

promise异步业务流程

异步函数 和同步函数在语法上其实几乎是一样(例如函数作值的高阶函数),都有返回 值,和异常处理,只是在效果上,异步函数的执行有一定的延迟。再又,当异步返回值再用来调用另一个异步函数,原callback类 API 的代码 很难维护。

编程都有任务,达到一种功能 或效果 ,promise对象(及其模板类)的效果 就是让「多步回调任务」流程更清晰。这个效果 已经 被写成了规范——Promises/A+

规范比较长和繁琐,优化多步回调流程主要是两步:第一,将「原始异步操作」包括成一个 promise对象(类);第二,实现一种thenable 行为接口,将多个promise 串连起来。

原始异步操作的包装

1 类初始化对象时 发起异步操作(包括结果 的处理发起) 2 实例 then方法 对异步结果的处理

thenable与链式

then 方法 默认返回 一个新 promise对象

var promise = new Promise(function(resolve, reject) {
  // do a thing, possibly async, then…

  if (/* everything turned out fine */) {
    resolve("Stuff worked!");
  }
  else {
    reject(Error("It broke"));
  }
});
promise.then(function(result) {
  console.log(result); // "Stuff worked!"
}, function(err) {
  console.log(err); // Error: "It broke"
});

研习手写promise的意义

现在(2023)async await已经相当成熟 了,研习 promise技术 还有意义 吗?其实还有,async await是基于 proimse的。意义主要有:

  • 1 更好使用promise工具 进行 异步业务编程(包括使用promise和包装一个原始异步操作)
  • 2 训练得更基础的 功能 编程 经验 (是个很好的例子)

常见疑问

1 reject 和 throw的区别

reject是 高级 自定义异常

2 自定义rejectHandler和 catch子句 用哪个

这里,“catch() 并没有任何特别之处,只是 then(undefined, func) 的添加剂,但它的可读性更好”

参考

1 https://web.dev/promises/

2 https://www.sitepoint.com/overview-javascript-promises/


  1. 原始 callback api 是特殊 的API,利用事件队列实现的 异步API。promise对象 是对 原始 callback api的 逻辑封装
裸男
Nakeman.cn 2023 Build by Gatsby and Tailwind, Deploy on Netlify.