跟我一起手撕 Promise
Promise 简介
Promise 是 JS 中异步编程的新解决方案,让我们方便地使用同步地方式书写异步代码。它也被称作 暂未存在确切值的替身
。
学习 Promise,你要先了解 JS 中的异步编程操作,比如 IO,AJAX,timeout 等。还有 js 事件循环模型。
Promise 出现前,获取异步任务的返回值,是通过设置回调函数。
下面假设了 3 个异步任务,任务之间存在依赖关系,每个异步请求的参数需要上一个异步任务返回的结果值。
1 | getData(params,function(data,err){ |
以特定执行顺序编排的异步任务,若使用纯回调函数来处理,会出现层层嵌套的书写格式。
我们用 Promise 改写一下:
1 | new Promise((resolve,reject)=>{ |
只需要在最后 catch 一下,就能捕获到上述每个环节的错误。
ES8 还提供了异步函数语法 async/await
1 | // 假设 promisify 是将异步任务转变为 promise 风格的函数 |
Promise 的特点
解耦了回调函数 ⭐⭐⭐⭐
promise 使用 then 方法指定回调函数
跟以前只要定义异步任务就一定要传入回调函数相比,简直就是新奇。
链式调用 ⭐
Promise 的 then 方法会返回新的 Promise 实例,供我们继续调用 then 方法指定下一个回调函数。
我们可以组成一条 Promise 链,串连我们的异步任务。
异常穿透 ⭐
单个 Promise 无需设置错误回调函数,在 Promise 链的末尾指定错误回调(onRejected),就能捕获到 Promise 链上出现的错误。
Promise 的内部实现
Promise 本质上是一个构造函数,通过传入执行器(executor)构造 Promise 对象,这个过程也称为封装异步操作。
要对 7 个问题有所认识。
- Promise 如何实现状态转换
- Promise 是先改状态再指定回调,还是先指定回调再改状态
- Promise 对象指定多个成功的回调,在状态改变时它们都会调用吗
- then 方法返回的 Promise 对象,其状态是由什么来决定的。
- Promise 如何实现链式调用
- Promise 如何实现异常穿透
- Promise 链如何中断
Promise 如何实现状态的转换
Promise 通过 3 种方式实现状态转换:
调用 resolve() 将状态改为 fulfilled
调用 reject() 将状态改为 rejected
throw 一个值,将状态改为 rejected
上述操作都在执行器函数(executor)中发生,并且 promise 的状态只能改变一次,改变状态的同时也会设置 promise 的结果值。
Promise 是先改状态再指定回调,还是先指定回调再改状态
Promise 既可以先改变状态,也可以先指定回调,这都不会影响运行 Promise 以及获取结果值。
Promise 对象指定多个成功的回调,在状态改变时它们都会调用吗
它们都会调用
then 方法返回的 Promise 对象,其状态是由什么来决定的。
由执行的回调函数的返回值决定:
回调函数返回非 Promise 对象,then 方法返回 fulfilled 的 Promise
回调函数返回 Promise 对象,then 方法返回的 Promise 拥有相同的状态和结果
回调函数 throw 一个值,then 方法返回 rejected 的 Promise
Promise 如何实现链式调用
调用 then 方法返回封装了新的异步任务的 Promise
Promise 如何实现异常穿透
当没有给 then 方法提供失败回调(onRejected)时,then 方法内部存在一个默认失败回调 reason => {throw reason}
,让 then 方法返回失败的 Promise,沿着 Promise 链传递到最后。
因此我们只需要给最后的一个 promise 指定失败回调即可捕获链上 Promise 的错误。
Promise 链如何中断
返回一个永久 pending 状态的 Promise 对象