Promise

365 阅读5分钟

Promise基础

常见的方法

  • 静态方法(类上的方法,通过Promise.XXX调用)
    • Promise.all()
    • Promise.resolve()
    • Promise.reject()
    • Promise.race()
  • 动态方法(原型上的方法,通过new Promise().XXX调用)
    • new Promise().then()
    • new Promise().catch()
    • new Promise().finally()

常见方法的用法

  • 1.new Promise() && new Promise().then()
    • 如图可以看出我们执行new Promise()的时候需要传进去一个executor执行器(函数),并且会立即执行;包含两个参数一个是resolve,一个是reject这两个参数依然是函数
    • executor函数会立即执行
    • 默认的状态是pendding
      • 注:Promise一共有三种状态,等待态:pendding 、成功态:fulfilled、失败态:rejected
    • 调用executor中的resolve方法代表成功态
    • 调用executor中的reject方法代表失败态
    • 可以从等待态(pendding)转化成成功态(fulfill)/失败态(reject);不可以从成功态/失败态向其他状态转化
  • 简易实现(仅支持同步)
function Promise(executor) { //1.传入executor
  let self = this;
  self.status = 'pending'; //5.在Promise内部中定义一个变量用来表示当前promise的状态 
  self.value = null; //6.记录resolve执行传入的value
  self.reason = null; //7.记录reject执行传入的reason
  function resolve(value) {  //3.调用resolve会传入一个value
    if (self.status !== 'pending') return;
    self.status = 'fulfilled';
    self.value = value;
  }

  function reject(reason) {  // 4.调用reject会传入一个reason
    if (self.status !== 'pending') return;
    self.status = 'rejected';
    self.reason = reason;
  }
  executor(resolve, reject)  //2.executor立即执行,传入两个方法resolve,reject
}

/*在Promise的原型上有一个then方法,then方法执行会传入两个函数onFulfilled && onRejected;*/
Promise.prototype.then = function (onFulfilled, onRejected) {
  let self = this;
  if (self.status === 'fulfilled') {  // promise的状态为fulfilled的时候onFulfilled执行
    onFulfilled(self.value);
  }
  if (self.status === 'rejected') {   // promise的状态为rejected的时候onFulfilled执行
    onRejected(self.reason);
  }
}
module.exports = Promise;
  • 升级实现(支持异步)
    • 上述的代码中我们实现了executor函数管理同步的方法,并没有对exctutor函数中管理的异步代码进行实现;即不是立即执行resolve 或者reject方法的时候(也就是说then方法中如果状态是pendding的时候我们怎么处理)
function Promise(executor) {
  let self = this;
  self.status = 'pending';
  self.value = null;
  self.reason = null;
  self.onFulfilledCallback = [];
  self.onRejectedCallback = [];

  function resolve(value) {
    if (self.status !== 'pending') return;
    self.status = 'fulfilled';
    self.value = value;
    self.onFulfilledCallback.forEach(fn => fn());
  }
  function reject(reason) {
    if (self.status !== 'pending') return;
    self.status = 'rejected';
    self.reason = reason;
    self.onRejectedCallback.forEach(fn => fn());
  }
  executor(resolve, reject);
}

Promise.prototype.then = function (onFulfilled, onRejected) {
  let self = this;
  if (self.status === 'fulfilled') {
    onFulfilled(self.value);
  }
  if (self.status === 'rejected') {
    onRejected(self.reason);
  }
  if (self.status === 'pending') {
    self.onFulfilledCallback.push(function () {
      onFulfilled(self.value);
    });

    self.onRejectedCallback.push(function () {
      onRejected(self.reason);
    })
  }
}

module.exports = Promise;

PeomiseA+实现

- 我们知道`Promise`最大的优点就是通过链式调用,解决回调地狱(恶魔金字塔)
- 链式调用的特点
    - 1.如果一个`then`中方法返回普通值,那么这个值会传递给下一个`then`的`onFullfilled`的输入
    - 2.如果一个`then`返回的是报错,那么这个错误信息会传递给下一个`then`的`onRejected`的输入
    - 3.如果返回的是一个`Promise`,那么这个`Promise`的成功或者失败决定下一个`then`调用的方法
    - 4.捕获错误机制
        - 默认会找离自己最近的`then`的失败,找不到继续向下找,最后找不到抛错
- 实现原理
    - 1.`promise`调用`then`方法会返回一个新的`Promise2`;
    - 2.拿到当前`then`方法中成功(`onFulfilled`)或者失败(`onRejected`)执行返回的结果`x`
    - 3.判断`then`方法返回的`promise2`和成功(`onFulfilled`)或者失败(`onRejected`)执行返回的结果`x`的关系
        - 1).如果`promise === x` 抛出循环引用错误
function Promise(executor) {
    let self = this;
    self.status = 'pending';
    self.value = null;
    self.reason = null;
    self.onFulfilledCallback = [];
    self.onRejectedCallback = [];
    function resolve(value) {
        if(value instanceof Promise){
            value.then(y=>{
                resolve(y);
            },r=>{
                reject(r);
            })
        }
        if (self.status === 'pending') {
             self.status = 'fulfilled';
             self.value = value;
             self.onFulfilledCallback.forEach(fn => fn());
        };
       
    }

    function reject(reason) {
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.reason = reason;
            self.onRejectedCallback.forEach(fn => fn());
        };
    }
    try{
        executor(resolve, reject);
    }catch(err){
        reject(err);
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
       reject(new TypeError('Circular Reference'));
    }
    if (x !== null && typeof x === 'object' || typeof x === 'function') {
        let called;
        try {
            let then = x.then;
            if (typeof then === 'function') {
                then.call(x, (y) => {
                    if (called) return;
                    called = true;
                    resolvePromise(promise2, y, resolve, reject);
                    // resolve(y);
                }, (r) => {
                    if (called) return;
                    called = true;
                    reject(r);
                })
            } else {
                resolve(x);
            }
        } catch (e) {
            if (called) return;
            called = true;
            reject(e);
        }
    } else {
        resolve(x);
    }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
    let self = this;
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ?onRejected : err => {throw err};
    let promise2 = new Promise(function (resolve, reject) {
        if (self.status === 'fulfilled') {
            setTimeout(() => {
                try {
                    let x = onFulfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            }, 0)
        }
        if (self.status === 'rejected') {
            setTimeout(() => {
                try {
                    let x = onRejected(self.reason);;
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            }, 0)

        }
        if (self.status === 'pending') {
            self.onFulfilledCallback.push(function () {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0)
            })
            self.onRejectedCallback.push(function () {
                setTimeout(() => {
                    try {
                        let x = onRejected(self.reason);;
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0)
            })
        }
    })
    return promise2;
}

/*实现一个Promise的defer对象*/
Promise.defer = function(){
    let dfd = {};
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}

module.exports = Promise;

注:上述代码实现过程中我们需要注意的几点说明:

  • 1.放到setTimeout中,原因是:当我们new Promise()执行的时候传入的excutor函数会立即执行,并且返回promise2,但是如果我们不放到setTimeout中我们是拿不到返回的promise2的,因为函数还没有执行完,并没有返回值,至于为什么放到setTimeou中可以拿到就涉及到了js代码的执行机制以及宏任务微任务的一些知识了,这里我就不做过多赘述了
  • 2.细心的朋友应该可以看到我代码中注释掉了resolve(y),而是递归调用了resolvePromise(promise2,y,resolve,reject),原因很简单就是当我们使用promise.then调用resolve()的时候可能继续传进去一个promise
  • 3.called,使用called的原因就更简单了,就是我们的promise可能会出现和别人的promise一起使用的情况,加called就是为了防止别人的代码没有实现非pending状态不能向其他状态转变的情况

验证是否符合PromiseA+规范

  • 使用官方提供的库promises-aplus-tests
  • 1.全局安装npm install promises-aplus-tests -g
  • 2.通过promises-aplus-tests xxx(需要测试的文件)命令进行测试
  • 3.要求测试的时候要有Promise.deferred,这就是我们代码中Promise.deffered的一个别名,加上即可(我这里就不添加了,你们贴过去测一下就会看到效果)

A+规范中的Promise常见方法实现

类上的方法

Promise.resolve()

Promise.resolve = function(value){
    return new Promise((reslove,reject)=>{
        reslove(value)
    })
}

Promise.reject()

Promise.reject = function(reason){
    return new Promise((resolve,reject)=>{
        reject(reason)
    })
}

Promise.all()

Promise.all = function(promises){
    return new Promise((resolve,reject)=>{
        let arr = [];
        let i = 0;
        function processData(index,value){
            arr[index] = value;
            if(++i === promises.length){
                resolve(arr);
            }
        }
        for(let i = 0; i < promises.length; i++){
            let current = promises[i];
            if(current !== null &&typeof current === 'object' || typeof current === 'function'){
                let then = current.then;
                if(typeof then === 'function'){
                    then(value=>{
                        processData(i,value);
                    },reject);
                }else{
                    processData(i,current);
                }
            }else{
                processData(i,current);
            }
        }
    })
}

Promise.race()

Promise.race = function(promises){
    return new Promise((resolve,reject)=>{
        for(let i = 0; i < promises.length; i++){
            let current = promises[i]; 
            if(current !== null && typeof current === 'object' || typeof current === 'function'){
                let then = current.then;
                if(typeof then === 'function'){
                    then(resolve,reject);
                }else{
                    resolve(current)
                }
            }else{
                resolve(current)
            }
        }
    })
}

原型上的方法

Promise.prototype.catch

Promise.prototype.catch = function(errCallback){
    return this.then(null,errCallback);
}

Promise.prototype.finally

Promise.prototype.finally = function(callback){
    return this.then(value=>{
        callback();
        return value;
    },reason=>{
        callback();
        return reason;
    })
}