手写一个Promise

es6中新增定义了一个Promise的类,本篇记录一下对Promise的深入学习过程。(长篇警告!!!)

首先纠正一个错误观点,“Promise是用来解决回调地狱的问题”,这只是语法糖,Promise真正解决的是数据依赖问题, 采用的方法依然是我们常挂嘴边的, "老掉牙"的 解耦。解的是 数据产生(发` )使用(收) 的耦, 也就是 的解耦.

接下来剖析一下他的解耦过程,分步实现
1、构造函数里传一个函数的两个参数(resolve, reject)
2、resolve 成功时执行的回调
3、reject 失败时执行的回调
4、三种状态 pending [待定] 初始状态、fulfilled [实现] 操作成功、rejected [被否决] 操作失败
5、Promise 对象方法 then
6、异步实现
7、onFulfilled 和 onRejected 的异步调用
8、值穿透
9、Promise 对象方法 catch
10、Promise 对象方法 all
11、Promise 对象方法 race
12、Promise 对象方法 resolve
13、Promise 对象方法 reject
14、Promise 对象方法 allSettled(新特性)
直接上代码

    class MyPromise {
        // 接收一个excutor函数
        constructor(excutor) {
            this.status = "pendding"; // 实现默认状态为 “pendding” 等待状态
            this.value = null; // 定义成功状态的值
            this.error = null; // 定义失败状态的值
            // 定义函数 resolve 用来改变Promise的状态为成功
            let resolve = res => {
                if (this.status === "pendding") {
                    this.value = res
                    this.status = "fulfilled" //改变状态为成功态
                }
            }
            // 定义函数 reject 用来改变Promise的状态为失败
            let reject = err => {
                if (this.status === "pendding") {
                    this.error = err
                    this.status = "rejected" //改变状态为失败态
                }
            }
            // 如果executor 执行报错,直接执行reject
            try {
                excutor(resolve, reject);
            } catch (err) {
                reject(err);
            }
        }
    }
    // log 一下
    console.log( new MyPromise((resolve, reject) => {
        // 没有resolve()或者reject() 状态并没有改变
    })); 

控制台输出结果为
在这里插入图片描述
接下来调用resolve方法

  class MyPromise {
        // 接收一个excutor函数
        constructor(excutor) {
            this.status = "pendding"; // 实现默认状态为 “pendding” 等待状态
            this.value = null; // 定义成功状态的值
            this.error = null; // 定义失败状态的值
            // 定义函数 resolve 用来改变Promise的状态为成功
            let resolve = res => {
                if (this.status === "pendding") {
                    this.value = res
                    this.status = "fulfilled" //改变状态为成功态
                }
            }
            // 定义函数 reject 用来改变Promise的状态为失败
            let reject = err => {
                if (this.status === "pendding") {
                    this.error = err
                    this.status = "rejected" //改变状态为失败态
                }
            }
            // 如果executor 执行报错,直接执行reject
            try {
                excutor(resolve, reject);
            } catch (err) {
                reject(err);
            }
        }
    }
    // log 一下
    console.log( new MyPromise((resolve, reject) => {
        resolve("成功了")
    }))

控制台输出结果为
在这里插入图片描述
此时status状态改变为fufilled状态,value值为“成功”

----------------------------------------------------------------------分割线-------------------------------------------------------------------
接下来补充then方法
Promise 中then 方法接收两个回调,表现形式为
new Promise( ).then(onFulfilled, onRejected)

 class MyPromise {
        // 接收一个excutor函数
        constructor(excutor) {
            this.status = "pendding"; // 实现默认状态为 “pendding” 等待状态
            this.value = null; // 定义成功状态的值
            this.error = null; // 定义失败状态的值
            // 定义函数 resolve 用来改变Promise的状态为成功
            let resolve = res => {
                if (this.status === "pendding") {
                    this.value = res
                    this.status = "fulfilled" //改变状态为成功态
                }
            }
            // 定义函数 reject 用来改变Promise的状态为失败
            let reject = err => {
                if (this.status === "pendding") {
                    this.error = err
                    this.status = "rejected" //改变状态为失败态
                }
            }
            // 如果executor 执行报错,直接执行reject
            try {
                excutor(resolve, reject);
            } catch (err) {
                reject(err);
            }
        }
        // 接收两个函数类型的参数
         then(onFullfilled,onRejected){
           // 如果为成功态,执行onFullfilled() 将value 传回去  value 在 resolve 时以赋值
            if(this.status === "fulfilled"){
                onFullfilled(this.value)
            }
           // 如果为失败态,执行onRejected() 将error传回去  error在 reject 时以赋值
            if(this.status === "rejected"){
                onRejected(this.error)
            }
        }
 }
 // 跑一下
 new MyPromise((resolve,reject)=>{
      resolve("成功") // 或者 reject("失败") 
 }).then(res=>{
      console.log(res) 
    },err=>{
      console.log(err)
   })
// 控制台输出结果为  成功 和 空 对应res  和 err

此时MyPromise已经可以解决大部分回调的问题了,但是对于setTimeout还是有些力不从心,原因是当 resolve 在 setTimeout 内执行,then 时 state 还是 pending 等待状态,具体可以翻看之前博客或者百度宏任务和微任务。
那怎么解决呢?
我们需要在 then 调用的时候,将成功和失败存到各自的数组,一旦 reject 或者 resolve,就调用它们。上代码

 class MyPromise {
        // 接收一个excutor函数
        constructor(excutor) {
            this.status = "pendding"; // 实现默认状态为 “pendding” 等待状态
            this.value = null; // 定义成功状态的值
            this.error = null; // 定义失败状态的值
            this.resolveQueue = []; // 成功存放的数组
            this.rejectQueue  = []; // 失败存放法数组
            // 定义函数 resolve 用来改变Promise的状态为成功
            let resolve = res => {
                if (this.status === "pendding") {
                    this.value = res
                    this.status = "fulfilled" //改变状态为成功态
                     //一旦 resolve 执行,调用成功数组的函数
                    this.resolveQueue.forEach(fn => fn())
                }
            }
            // 定义函数 reject 用来改变Promise的状态为失败
            let reject = err => {
                if (this.status === "pendding") {
                    this.error = err
                    this.status = "rejected" //改变状态为失败态
                    //一旦 reject 执行,调用失败数组的函数
                    this.rejectQueue.forEach(fn => fn())
                }
            }
            // 如果executor 执行报错,直接执行reject
            try {
                excutor(resolve, reject);
            } catch (err) {
                reject(err);
            }
        }
        // 接收两个函数类型的参数
         then(onFullfilled,onRejected){
           // 如果为成功态,将onFullfilled(this.value)push 到resolveQueue 成功数组里面
            if(this.status === "fulfilled"){
               this.resolveQueue.push(() => {
                    onFullfilled(this.value)
                })
            }
           // 如果为失败态,将onRejected(this.error))push 到 rejectQueue 失败 数组里面
            if(this.status === "rejected"){
               this.rejectQueue.push(() => {
                    onRejected(this.error)
                })
            }
            //当状态status为pendding 时
            if (this.status === "pending") {
                //onFullfilled 传入到成功数组
                this.resolveQueue.push(() => {
                    onFullfilled(this.value)
                })
                //onRejected 传入到失败数组
                this.rejectQueue.push(() => {
                    onRejected(this.error)
                })
            }
        }
 }

----------------------------------------------------------------分割线-------------------------------------------------------------------------
回到开头,我们都知道promise可以用来解决回调地狱的问题,写法是new Promise().then().then() ,这种链式调用关系,那么我们在MyPromise上实现一下
方法很简单,在then方法中 return 一个新的 promise 就行了
来吧,展示

 class MyPromise {
        // 接收一个excutor函数
        constructor(excutor) {
            this.status = "pendding"; // 实现默认状态为 “pendding” 等待状态
            this.value = null; // 定义成功状态的值
            this.error = null; // 定义失败状态的值
            this.resolveQueue = []; // 成功存放的数组
            this.rejectQueue  = []; // 失败存放法数组
            // 定义函数 resolve 用来改变Promise的状态为成功
            let resolve = res => {
                if (this.status === "pendding") {
                    this.value = res
                    this.status = "fulfilled" //改变状态为成功态
                     //一旦 resolve 执行,调用成功数组的函数
                    this.resolveQueue.forEach(fn => fn())
                }
            }
            // 定义函数 reject 用来改变Promise的状态为失败
            let reject = err => {
                if (this.status === "pendding") {
                    this.error = err
                    this.status = "rejected" //改变状态为失败态
                    //一旦 reject 执行,调用失败数组的函数
                    this.rejectQueue.forEach(fn => fn())
                }
            }
            // 如果executor 执行报错,直接执行reject
            try {
                excutor(resolve, reject);
            } catch (err) {
                reject(err);
            }
        }
        // 接收两个函数类型的参数
         then(onFullfilled,onRejected){
            let promise2;
            promise2 = new MyPromise((resolve, reject) => {
                if (this.status === "fulfilled") {
                    let x = onFullfilled(this.value);
                    // resolvePromise函数,处理自己return的Mypromise和默认的promise2的关系
                    resolvePromise(promise2, x, resolve, reject);
                }
                if (this.status === "rejected") {
                    let x = onRejected(this.value);
                    resolvePromise(promise2, x, resolve, reject);
                }
                if (this.status === "pending") {
                    this.resolveQueue.push(() => {
                        let x = onFullfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    })
                    this.rejectQueue.push(() => {
                        let x = onRejected(this.error);
                        resolvePromise(promise2, x, resolve, reject);
                    })
                }
            });
            // 返回 promise,达成链式效果
            return promise2;
        }
 }
 // 实现resolvePromise方法
 function resolvePromise(promise2, x, resolve, reject){
  // 循环引用报错
  if(x === promise2){
    // reject 报错抛出
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  
  // 锁,防止多次调用
  let called;
  
  // x 不是 null 且 x 是对象或者函数
  if (x != null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      // A+ 规定,声明then = x的then方法
      let then = x.then;
      // 如果then是函数,就默认是promise了
      if (typeof then === 'function') { 
        // then 执行 第一个参数是 this 后面是成功的回调 和 失败的回调
        then.call(x, y => {
          // 成功和失败只能调用一个
          if (called) return;
          called = true;
          // resolve 的结果依旧是 promise 那就继续递归执行
          resolvePromise(promise2, y, resolve, reject);
        }, err => {
          // 成功和失败只能调用一个
          if (called) return;
          called = true;
          reject(err);// 失败了就失败了
        })
      } else {
        resolve(x); // 直接成功即可
      }
    } catch (e) { // 走到 catch 也属于失败
      if (called) return;
      called = true;
      // 取then出错了那就不要在继续执行了
      reject(e); 
    }
  } else {
    resolve(x);
  }
}
   // 还是实例化一下
    new MyPromise((resolve,reject)=>{
        resolve("进入第一个then")
    }).then(res=>{
        console.log(res);  // 进入第一个then
        return new MyPromise((resolve,reject)=>{
            resolve("进入第二个then")
        })
    }).then(res=>{
        console.log(res); //进入第二个then
    })

这段代码,特别是函数部分,建议反复理解,这是Promise的重难点!!!
然后我们再补充上onFulfilled 和 onRejected 的异步调用

  then(onFullfilled,onRejected){
            let promise2;
            promise2 = new MyPromise((resolve, reject) => {
                if (this.status === "fulfilled") {
                    setTimeout(()=>{  //统一放入异步队列
                       let x = onFullfilled(this.value);
                       // resolvePromise函数,处理自己return的Mypromise和默认的promise2的关系
                       resolvePromise(promise2, x, resolve, reject);
                    })
                }
                if (this.status === "rejected") {
                    setTimeout(()=>{
					   let x = onRejected(this.value);
                       resolvePromise(promise2, x, resolve, reject);
					})
                }
                if (this.status === "pending") {
                       this.resolveQueue.push(() => {
                          setTimeout(()=>{ 
                            let x = onFullfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                         })
                       })
                       this.rejectQueue.push(() => {
                         setTimeout(()=>{
					         let x = onRejected(this.value);
                             resolvePromise(promise2, x, resolve, reject);
					     })
                      })   
                }
            });
            // 返回 promise,达成链式效果
            return promise2;
        }

-----------------------------------------------------------------------分割线-----------------------------------------------------------------
值穿透

new Promise((resolve, reject)=>{
    resolve('hello word');
}).then().then().then().then().then().then().then((res)=>{ 
    console.log(res);
})
// 当执行了多个then 的情况,我们希望最后一个then打印出 hello word 字样
// 同样也很好解决,只要判断then的参数是不是以个function 就行了。onFulfilled 如果不是函数,就忽略 onFulfilled,直接返回 value!
//  onRejected  同上
 then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err;}    
    <!--... 省略-->
 }

-----------------------------------------------------------------------分割线-----------------------------------------------------------------
实现catch方法
说白了就是直接调用then方法只传入onRejected而已,加到代码上

class MyPromise {
        constructor(excutor) {
        <!--... 省略-->
        }
        then(onFullfilled,onRejected){
          <!--... 省略-->
        }
        catch(){
          return this.then(undefined,onRejected(this.error))
        }
}

-----------------------------------------------------------------------分割线-----------------------------------------------------------------
实现all 方法
经常会有面试官问我们,promise.all()是怎么用的,或者说,当我需要发送两个请求,当两个请求都成功的时候再改变状态,展示或隐藏时,可以通过什么方式实现。
我们来贴一下promise.all()的定义

Promise.all() 接收一个数组作为参数,该方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个失败 promise 的结果。

简短截说就是promise.all()中传入一个数组,数组内的所有promise的状态都为成功时,promise.all()就会执行

 MyPromise.all = function (arr) {
        return new MyPromise(function (resolve, reject) {
            let arr = [];
            let count = 0;
            for (let i = 0; i < arr.length; i++) {
                let p = arr[i];
                p.then(function (value) {
                    arr.push(value);
                    count++;
                    //当所有的promise对象都成功返回时,判断是否是最后一个如果是则返回保存的数组
                    if (arr.length === count) {
                        resolve(arr)
                    }
                }).catch(function (e) {
                    reject(e);
                })
            }
        })
    }
  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值