Promise 类基础逻辑
Promise 可以解决的问题
- 解决回调地狱问题,不会导致难以维护
- 合并多个异步请求,节约时间
基础逻辑
new Promise
:Promise 是一个类,在执行这个类的时候,需要传递一个执行器进去,执行器会立即执行Promise 中有三种状态,分别为:成功(fulfilled)、失败(rejected)、等待(pending),一旦状态确定就不可更改
如果状态不是等待,需要阻止程序向下执行
resolve 和 reject 函数是用来更改状态的
把 resolve 和 reject 定义为箭头函数是为了让函数内部的 this 指向,指向类的实例对象(Promise)
then 方法内部做的事情就是判断状态,如果状态是成功,就调用成功的回调函数,如果状态是失败,就调用失败的回调函数。then 方法定义在原型对象上的
then 成功回调有一个参数,表示成功之后的值,then 失败回调有一个参数,表示失败的原因
1 | // 可复用且有提示 |
之后对其进行验证
1 | const MyPromise = require('./myPromise') |
加入 then 方法和异步逻辑
问题1: 如果要给 resolve 或 reject 包裹一层 setTimeout,发现什么都没有输出
- 主线程是不会等待异步 setTimeout 执行完成的,then 会立即执行
- 由于当前 Promise 执行到 then 时状态为 等待态,而现在只判断成功与失败的状态,所以会什么都不输出
1 | let promise = new MyPromise((resolve, reject) => { |
问题2: 当 then 方法被多次调用时,每一个 then 方法中传递的回调函数都是要执行的
- 同步:如果调用 then 方法时,已经知道 promise 状态为 成功态或失败态,就可以直接调用回调即可
- 异步:如果调用 then 方法时,promise 状态为 等待态,每一个 then 方法的回调函数都应该存储起来,当状态为成功或失败时,再依次调用回调函数
1 | let promise = new MyPromise((resolve, reject) => { |
接下来处理上面出现的两个问题
1 | const PENDING = 'pending' |
实现 then 链
链式调用
then 方法是可以被链式调用的,后面 then 方法的回调函数拿到上一个 then 方法的回调函数的返回值
1 | let promise = new MyPromise((resolve, reject) => { |
接下来需要实现如下需求
实现 then 方法链式调用(then 方法返回一个 promise)
如何把上一个 then 回调函数的返回值传递给下一个 then 方法的回调函数
这里需要判断回调函数返回值是普通值还是 Promise 对象
- 如果是普通值,直接调用 resolve
- 如果是 promise 对象,查看 promise 对象返回的结果,根据结果决定调用 resolve 还是 reject
1 | class MyPromise { |
循环引用
如果在 p1 里返回 p1 这个 Promise,就会发生循环调用
1 | let promise = new MyPromise((resolve, reject) => { |
注意: 举个例子 var obj = { n: 10, x: obj.n *10 }
,因为 obj 还没有创建完,而在创建属性 x 时是获取不到 obj.n 的。因为全局作用于只声明了 obj,却没有赋值(obj -> undefiend
),promise 也是有这样的情况的,我们可以使用异步任务,让其赋值完成
1 | class MyPromise { |
捕获错误
当执行器(
executor
)中代码执行时发生错误,将 promise 状态改为失败态回调函数在执行时发生错误,这个错误要在下个 then 方法的回调函数中捕获到
当代码为失败状态或等待态也需要用
try...catch
包裹当代码为等待态时,如果碰到异步,不能把回调函数直接 push 到数组里,这样没有办法对其进行处理,我们可以 push 一个函数进去,函数里面调用成功或失败回调
这时就可以对进行异步和错误捕获处理了
1 | const PENDING = 'pending' |
then 方法参数变为可选
链式调用 then 时,即使不传递参数也会依次向后传递
1 | const promise = new MyPromise((resolve, reject) => { |
其实就是判断 successCallback
与 failCallback
是否存在,如果不存在给它进行赋值
1 | class MyPromise { |
其他方法实现
Promise.all
- all 方法是通过类直接调用,所以 all 方法是一个静态方法
- all 传入的必须是一个数组,如果数组中某一项不是 Promise 实例,需要把它转换为成功的 Promise 实例
- 之后每一项依次执行,只要有一项失败返回就是失败的,必须全部成功才是成功
1 | function p1() { |
注意:
- 返回结果的顺序跟传入的顺序一致(不能使用 push,因为不能确定谁先到,需要使用索引)
- for 循环执行就是一瞬间的,但是里面可能存在异步操作,需要等待所有都执行完,再执行 resolve 操作
1 | class MyPromise { |
Promise.resolve
1 | function p1() { |
- 如果参数是 Promise 实例,那么
Promise.resolve
将不做任何修改,原封不动地返回这个实例 - 有如果参数是普通值,就直接将值转换为成功的 Promise 实例
1 | class MyPromise { |
finally
1 | function p1() { |
- 无论当前 Promise 最终状态是成功的还是失败的,finally 中的回调函数始终都会被执行一次
- 在 finally 方法后面链式调用 then 方法拿到当前这个 promise 最终返回的结果
1 | class MyPromise { |
catch
- 处理当前 promise 为失败状态,内部调用的也是 then 方法(只注册失败回调)
1 | class MyPromise { |
完整版
1 | const PENDING = 'pending' |