如何手写一个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)
的添加剂,但它的可读性更好”
参考
2 https://www.sitepoint.com/overview-javascript-promises/
- 原始 callback api 是特殊 的API,利用事件队列实现的 异步API。promise对象 是对 原始 callback api的 逻辑封装↩