一文弄懂Promise 原理

323 阅读5分钟

之前也看过好多promise 的文章并手写过promise,只是会用,知道promise 的原理,但是它的思想到底是什么呢,直到我看到了函数式编程中的函子,才找到了答案, Promise 就是对函数式编程函子的应用,之前异步解决方案说过,异步方案的目的就是让异步代码写起来像同步,所以promise实现同步链式调用,这不就是函子的思想么

回顾一下函子的概念,点击这个地址

1.函子

class Functor{
    constructor(value) {
        this._value = value
    }
    map(fn) {
        return Functor.of(fn(this._value))
    }    
    static of(value) {
        return new Functor(value)
    }
}    

上面就是函子,核心思想就是

  • 每个函子Functor都是一个新的对象

  • 对象的原型链上有 map 函数

  • 通过 map 中传递进去的函数fn去处理函子保存的数据,用得到的值去生成新的函子,从而实现链式调用的值传递

2. 实现异步链式调用

上面函子的是同步链式调用,如果想要异步链式调用我们就需要传入一个执行器 excutor 包含一个参数 resolve 函数,延迟执行异步回调,

1. 第一步、根绝函子的思想,我们首先的拿到传入的值

class MyPromise{
  constructor(excutor){
    this._value = null
    let resolve = (value) => {
      this._value = value
      console.log(this._value);
    }
    excutor(resolve)
  }
}
new MyPromise((resolve) => {
    setTimeout(()=>{
        resolve('resolve')
    }, 1000)
})

如上图所示我们 拿到了 外部传入的 resolve的值,这里需要注意一下 执行器 excutor 是由外向内传值,resolve 是 promise 内部定义,外部调用,这样就可以修改内部的_value值

2.第二步、还是根据函子的思想,拿到外部传入的值之后,我们要通过map 传入的fn 去处理拿到的值,但是由于是 fn 有可能是异步的,所以我们可以找一个队列存储下来,并将fn返回结果交给下一个 函子的 resolve 解决

为了理解方便我们直接把map 写成then

class MyPromise{
  constructor(excutor){
    this._value = null
    this.callback = []
    let resolve = (value) => {
      this._value = value
      this.callback && this.callback.forEach( item => item())
    }
    excutor(resolve)
  }
  then(fn) {
    return new MyPromise((resolve) => {
      this.callback.push(() => {
        let data = fn(this._value)
        resolve(data)
      })
    })
  }
}
var promise = new MyPromise( (resolve) => {
  setTimeout( () => {
    resolve(1)
  }, 1000)
})
promise.then((res) => {
    return res + 1
}).then( res => {
    console.log(res)
    return res + 1
})

通过then方法向下手机 resolve 的callback,在 resolve 时候执行收集到的callback

3. 为MyPromise 函子增加失败回调 和 状态

有时候在异步回调时候不能拿到结果,需要返回一个错误信息,所以需要增加一个错误回调来处理异常,写法同成功回调

我们知道  promise 的初始状态是 pending ,完成状态是 fulfilled, 失败状态是 rejected, 在成功或者失败的callback修改 状态

const PENDING = 'pending'
const FULFILED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise{
  constructor(excutor){
    this._value = null
    this._reason = null
    this._status = PENDING

    this.resolveCallback = []
    this.rejectCallback = []

    let resolve = (value) => {
      this._value = value
      this._status = FULFILED
      this.resolveCallback && this.resolveCallback.forEach( item => item())
    }
    let reject = (reason) => {
      this._reason = reason
      this._status = REJECTED
      this.rejectCallback && this.rejectCallback.forEach( item => item())
    }

    excutor(resolve, reject)
  }
  then(resolveFn, rejectFn) {
    return new MyPromise((resolve, reject) => {
      this.resolveCallback.push(() => {
        let data = resolveFn(this._value)
        resolve(data)
      })
      this.rejectCallback.push(() => {
        let data = rejectFn(this._reason)
        reject(data)
      })
    })
  }
}

4.  增加状态的判断,一级 then 中 返回一个MyPromise的处理方式

**看到有些文章写异步处理, 用的是 setTimout 这里其实是不合理的,因为 setTimeout是一个宏任务,**而promise是一个微任务,用宏任务实现一个微任务,本质上是不合理的

所以我们用queueMicrotask,它其实也是用promise实现的一种微任务,这里为了通过后面promise A+的检测,还是使用queueMicrotask

resolve = (value) => {
    if( this._status == PENDING ) {
      queueMicrotask(() => {
        this._value = value
        this._status = FULFILED
        this.resolveCallback && this.resolveCallback.forEach( item => item())
      }, 0)
    }
  }
  reject = (reason) => {
    if( this._status == REJECTED ) {
      queueMicrotask(() => {
        this._reason = reason
        this._status = REJECTED
        this.rejectCallback && this.rejectCallback.forEach( item => item())
      })
    }
  }

这里加了 _status 的判断,只有在 pending状态下才能修改 MyPromise的状态, 然后then方法中添加了 状态的判断,只有pending 才手机回调,其他状态直接执行对应状态的函数

then(resolveFn, rejectFn) {    
  return new MyPromise((resolve, reject) => {      
    if( this._status == PENDING ) {        
      this.resolveCallback.push(() => {          
        let data = resolveFn(this._value)         
        resolve(data)        
      })       
      this.rejectCallback.push(() => {         
        let data = rejectFn(this._reason)          
        reject(data)        
      })      
    }else if( this._status == FULFILED ) {        
      let data = resolveFn(this._value)        
    if( data instanceof MyPromise ) {          
      data.then( resolve, reject)        
    }else{          
      resolve(data)        }      
    }else if( this._status == REJECTED ) {        
      reject(this._reason)      
    }    
  })   
}

这里遇到如果MyPromise 返回的是一个MyPromise 对象时候需要将 这个返回的MyPromise的值传递下去

if( data instanceof MyPromise ) {
  data.then( resolve, reject)
}

这样一个基本的Promise 就写好了,当然还有一系列判断要写,这个可以以后再完善,这里主要是理解Promise的原理

3.添加静态方法和实例方法

静态方法主要有Promise.resolve, Promise.reject, Promise.all, Promise.race,下面让我们实现他们

1.Promise.resolve, 这个其实很简单了就是生产一个状态为 fulfiled的Promise对象

static resolve(parameter) {
  if(parameter instanceof MyPromise) {
    return parameter  }
  return new MyPromise(resolve=>{
resolve(parameter)      })
}

2.Promise.reject

static reject(reason) {
  return new MyPromise((resolve, reject) => {
    reject(reason)
  })
}

3.Promise.all, 传入一个promise 对象list 列表中每个promise都返回结果才返回结果

static all (promiseList) {
    return new MyPromise((resolve, reject) => {
      const result = [];
      const length = promiseList.length;
      let count = 0;

      if (length === 0) {
        return resolve(result);
      }

      promiseList.forEach((promise, index) => {
        MyPromise.resolve(promise).then((value) => {
          count++;
          result[index] = value;
          if (count === length) {
            resolve(result);
          }
        }, (reason) => {
          reject(reason);
        });
      });
    });

  }

4.Promise.race ,race 顾名思义就是赛跑,list 中哪个结果返回的快就取哪个

static race (promiseList) {
    return new MyPromise((resolve, reject) => {
      const length = promiseList.length;

      if (length === 0) {
        return resolve();
      } else {
        for (let i = 0; i < length; i++) {
          MyPromise.resolve(promiseList[i]).then((value) => {
            return resolve(value);
          }, (reason) => {
            return reject(reason);
          });
        }
      }
    });
  }

实例方法主要有catch 和 finally

1.promise.catch 就是捕获错误回调

catch (onRejected) {
    // 只需要进行错误处理
    this.then(undefined, onRejected);
}

2.promise.finally 不管promise 最后状态是 成功还是失败都会执行这个回调

finally (fn) {
    return this.then((value) => {
      return MyPromise.resolve(fn()).then(() => {
        return value;
      });
    }, (error) => {
      return MyPromise.resolve(fn()).then(() => {
        throw error
      });
    });
}

4.验证通过Promise A+ 规范测试

1.安装 promises-aplus-tests

npm install promises-aplus-tests --save-dev

2. 添加测试代码

MyPromise.deferred = function () {
  var result = {};
  result.promise = new MyPromise(function (resolve, reject) {
    result.resolve = resolve;
    result.reject = reject;
  });

  return result;
}
module.exports = MyPromise;

3. package.json 中添加运行的命令

"scripts": {
"test": "promises-aplus-tests '文件名称'"
}

4. 运行 npm run test

如果运行之后看到这个画面,恭喜你成功写了一个Promise

参考链接:www.cnblogs.com/chenwenhao/…
juejin.cn/post/694531…