手把手教你实现Promise(一)(基于Promise A+规范)

使用Promise能解决回调地狱多个异步请求等问题。那么它是怎么实现的呢?让咱们一块儿来实现一下吧jquery

同步调用的实现

首先,咱们要知道:npm

  • Promise是一个类
  • new Promise 时,会返回一个promise的对象,它会传一个执行器(executor),这个执行器是当即执行的
  • 另外每一个promise实例上都会有一个then方法,参数分别是成功(有成功的值)和失败(有失败的原用)两个方法
  • promise有三个状态:成功态,失败态,等待态。
  • 默认状态是等待态,等待态能够变成成功态或失败态
  • 一旦成功或失败就不能再变会其余状态了 知道这些就能够先简单实现一下了
class Promise {
  constructor(executor) { //executor执行器
    this.status = 'pending'; //默认等待状态
    this.value = undefined; //成功的值
    this.reason = undefined //失败的原用

    let resolve = (value) => {
      if (this.status === 'pending') {
        this.status = 'resolved'; //成功
        this.value = value;
      }
    }
    let reject = (reason) => {
      if (this.status === 'pending') {
        this.status = 'rejected'; //失败
        this.reason = reason;
      }
    }
    executor(resolve, reject); //默认上执行器执行
  }
  then(onFufilled, onRejected) {
    if (this.status === 'resolved') { //成功态
      onFufilled(this.value);
    }
    if (this.status === 'rejected') { //失败态
      onRejected(this.reason);
    }
  }
}
module.exports = Promise
复制代码

以上,咱们就简单的实现了一个同步的promise。 测试一下吧数组

let Promise = require('./myPromise.js')
let promise = new Promise((resolve, reject) => {
    resolve('hello')
})
promise.then((data) => {
    console.log(data)
}, (err) => {
    console.log(err)
})
复制代码

打印结果:promise

可是,咱们知道,promise主要解决的是异步回调问题。因此,异步调用必须实现起来。bash

异步调用的实现

当异步调用时,当调用实例的then时,状态可能还处于pending状态,这时咱们须要在实例上定义两个存放成功和失败方法的数组,把须要执行的方法分别放到对应的数组里,等到异步时间到达的时候,再去执行对应数组里的方法。异步

class Promise {
  constructor(executor) { //executor执行器
    this.status = 'pending'; //默认等待状态
    this.value = undefined; //成功的值
    this.reason = undefined //失败的原用
   + //存放then成功,失败的回调的数组
    this.onResovleCallbacks = [];
    this.onRejectedCallbacks = [];
    
    let resolve = (value) => {
      if (this.status === 'pending') {
        this.status = 'resolved'; //成功
        this.value = value;
    +    this.onResovleCallbacks.forEach(fn => fn());
      }
    }
    let reject = (reason) => {
      if (this.status === 'pending') {
        this.status = 'rejected'; //失败
        this.reason = reason;
    +    this.onRejectedCallbacks.forEach(fn => fn());
      }
    }
    executor(resolve, reject); //默认上执行器执行
  }
  then(onFufilled, onRejected) {
    if (this.status === 'resolved') { //成功态
      onFufilled(this.value);
    }
    if (this.status === 'rejected') { //失败态
      onRejected(this.reason);
    }
+    if (this.status === 'pending') {
      this.onResovleCallbacks.push(() => {
        onFufilled(this.value)
      });
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason)
      })
    }
  }
}

module.exports = Promise
复制代码

以上,咱们就实现了promise的异步调用。 测试一下吧函数

let Promise = require('./myPromise.js')
let promise = new Promise((resolve, reject) => {
    setTimeout(function(){
        resolve('hello')
    },100)
})
promise.then((data) => {
    console.log(data)
}, (err) => {
    console.log(err)
})
复制代码

打印结果: 测试

非正常执行处理

当执行的时候抛出异常时,咱们应该让它当状态变为rejected,去执行then的错误方法。 这时候,须要在执行器执行的时候 捕获一下错误,并做出rejected处理ui

try {
      executor(resolve, reject);
    } catch (e) { //捕获到异常时,直接走失败
      reject(e);
    }
复制代码

测试一下吧this

let Promise = require('./myPromise.js')
let promise = new Promise((resolve, reject) => {
   throw new Error('❌')
})
promise.then((data) => {
    console.log(data)
}, (err) => {
    console.log(err)
})
复制代码

打印结果:

链式调用的实现

说到链式调用,我们接触最多的就是jquery,jquery实现链式调用是靠的是返回this,promise实现链式调用是否是也返回this呢?答案是,NO !它实现链式调用靠的是返回一个新的promise。 在then方法里,不管promise处于哪一种状态,执行完后,都返回一个新的promise。

then(onFufilled, onRejected) {
  +  let promise2; //返回的新promise
  +  promise2 = new Promise((resolve, reject) => {
      if (this.status === 'resolved') {
        onFufilled(this.value);
      }
      if (this.status === 'rejected') { 
        onRejected(this.reason);
      }
      if (this.status === 'pending') {
        this.onResovleCallbacks.push(() => {
          onFufilled(this.value)
        });
        this.onRejectedCallbacks.push(() => {
          onRejected(this.reason)
        })
      }
    });
  +  return promise2;
  }
复制代码

链式调用之错误状况

在then中,不管是成功的回调仍是失败的回调,只要返回告终果就会走下一个then中的成功,若是有错误,就会走下一个then的失败回调。即:下一个then的状态跟上一个then执行时候的状态无关。 因此,在then执行的时候,onFufilled, onRejected可能会出错,这时候,咱们须要捕获错误,并处理成失败

promise2 = new Promise((resolve, reject) => {
      if (this.status === 'resolved') {
        try {
          onFufilled(this.value);
        } catch (e) {
          reject(e)
        }
      }
      if (this.status === 'rejected') {
        try {
          onRejected(this.reason);
        } catch (e) {
          reject(e)
        }
      }
      if (this.status === 'pending') {
        this.onResovleCallbacks.push(() => {
          try {
            onFufilled(this.value)
          } catch (e) {
            reject(e);
          }
        });
        this.onRejectedCallbacks.push(() => {
          try {
            onRejected(this.reason)
          } catch (e) {
            reject(e);
          }
        })
      }
    });
复制代码

测试一下吧

let promise = new Promise((resolve, reject) => {
    resolve('hello')
})
promise.then((data) => {
    console.log(data)
    throw new Error('🙅')
}, (err) => {
    console.log(err)
}).then((data) => {
    console.log(data)
}, (err) => {
    console.log('🙅' + err)
})
复制代码

打印结果:

链式调用之兼容多种状况

  • 若是第一个promise返回一个普通值,直接将这个返回值,传递给下一次then的resolve。
  • 若是第一个promise返回一个promise,须要等待返回的这个promise执行后的结果,传给下一次then 处理第一次promise执行后的返回值x,then方法的每一个状态都须要处理一下:
try {
		  //x是上一个promise返回值,多是一个普通值,也多是一个promise;x也多是别人的promise,咱们能够写一个方法,统一处理
          let x=onFufilled(this.value);
          //入参:下一次then的实例promise2,此次返回值x,promise2的成功方法,promise2的失败方法
          resolvePromise(promise2,x,resolve,reject);
        } catch (e) {
          reject(e)
        }
复制代码

下面来实现resolvePromise,用来处理多套promise共用的状况:

/*
*	resolvePromise 
*	@Parameters
*		promise2:	下一次then的实例promise2
*		x:			此次返回值x
*		resolve:	promise2的成功方法
*		reject:	promise2的失败方法
*/
function resolvePromise(promise2, x, resolve, reject) {
      //x多是别人的promise,因此尽量的容许别人瞎写
      if (promise2 === x) { //返回的结果和promise是同一个,那么永远不会成功
        return reject(new TypeError('循环引用'));
      }
      let called;//是否调用过成功或失败
      // 看x是否是promise。promise应该是一个对象
      if (x != null && (typeof x === 'object' || typeof x === 'function')) { //多是promise
        try {
          let then = x.then; // 若是是对象 就试着取一下then方法 若是有then,认为它是promise
          if (typeof then === 'function') { // 若是then是函数,是promise
            then.call(x, y => {
              // 成功和失败只能调用一个
              if (called) return;
              called = true;
              // resolve的结果依旧是promise 那就继续解析
              resolvePromise(promise2, y, resolve, reject);
            }, r => {
              if (called) return;
              called = true;
              reject(r); // 失败了就失败了
            })
          } else {
            resolve(x); // 直接成功便可
          }
        } catch (e) { // 取then出错了那就不要在继续执行了
          if (called) return;
          called = true;
          reject(e);
        }
      } else { //普通值 让promise2直接变成成功态
        resolve(x);
      }
    };
复制代码

测试一下吧

  • 返回一个普通值
let promise = new Promise((resolve, reject) => {
    resolve('hello')
})
promise.then((data) => {
    console.log(data)
    throw new Error('🙅')
}, (err) => {
    console.log(err)
}).then((data) => {
    console.log(data)
}, (err) => {
    console.log('🙅' + err)
})
复制代码

打印结果:

  • 返回一个promise
let promise = new Promise((resolve, reject) => {
    resolve('hello')
})
promise.then((data) => {
    console.log(data)
    return new Promise((resolve, reject) => {
        resolve('👋')
    })
}, (err) => {
    console.log(err)
}).then((data) => {
    console.log(data)
}, (err) => {
    console.log('🙅' + err)
})
复制代码

打印结果:

以上,咱们的promise好像已经差很少了,可是还有一个问题,须要处理。源码能够在hen中实现什么都不传。promise中管这种现象叫,值的穿透。 所以,咱们须要在then方法里,对then方法的入参进行容错处理:

onFufilled = typeof onFufilled === 'function' ? onFufilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err};
复制代码

测试一下吧

let Promise = require('./myPromise.js')
let promise = new Promise((resolve, reject) => {
    resolve('hello')
})
promise.then().then((data) => {
    console.log(data)
}, (err) => {
    console.log('🙅' + err)
})
复制代码

打印结果:

另外,promise规范中要求,全部的onFufilledonRejected都须要异步执行,若是不加异步可能形成测试的不稳定性,因此咱们给执行这两个方法执行的地方都加上异步方法。

if (this.status === 'resolved') {
        setTimeout(() => {
          try {
            let x=onFufilled(this.value);
            resolvePromise(promise2,x,resolve,reject);
          } catch (e) {
            reject(e)
          }
        }, 0);
      }
复制代码

Promise实现

class Promise {
  constructor(executor) { //executor执行器
    this.status = 'pending'; //默认等待状态
    this.value = undefined; //成功的值
    this.reason = undefined //失败的原用
    this.onResovleCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = (value) => {
      if (this.status === 'pending') {
        this.status = 'resolved'; //成功
        this.value = value;
        this.onResovleCallbacks.forEach(fn => fn());
      }
    }
    let reject = (reason) => {
      if (this.status === 'pending') {
        this.status = 'rejected'; //失败
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    }
    try {
      executor(resolve, reject); //默认上执行器执行
    } catch (e) { //捕获到异常时,直接走失败
      reject(e);
    }
  }
  then(onFufilled, onRejected) {
    onFufilled = typeof onFufilled === 'function' ? onFufilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : err => {
      throw err
    };
    function resolvePromise(promise2, x, resolve, reject) {
      //x多是别人的promise,因此尽量的容许别人瞎写
      if (promise2 === x) { //返回的结果和promise是同一个,那么永远不会成功
        return reject(new TypeError('循环引用'));
      }
      //
      let called;
      // 看x是否是promise。promise应该是一个对象
      if (x != null && (typeof x === 'object' || typeof x === 'function')) { //多是promise
        try {
          let then = x.then; // 若是是对象 我就试着取一下then方法 若是有then,认为它是promise
          if (typeof then === 'function') { // then是函数,是promise
            then.call(x, y => {
              // 成功和失败只能调用一个
              if (called) return;
              called = true;
              // resolve的结果依旧是promise 那就继续解析
              resolvePromise(promise2, y, resolve, reject);
            }, r => {
              if (called) return;
              called = true;
              reject(r); // 失败了就失败了
            })
          } else {
            resolve(x); // 直接成功便可
          }
        } catch (e) { // 取then出错了那就不要在继续执行了
          if (called) return;
          called = true;
          reject(e);
        }
      } else { //普通值 让promise2直接变成成功态
        resolve(x);
      }
    };
    let promise2; //返回的新promise
    promise2 = new Promise((resolve, reject) => {
      if (this.status === 'resolved') {
        setTimeout(() => {
          try {
            let x = onFufilled(this.value); //x是上一个promise返回值,多是一个普通值,也多是一个promise;x也多是别人的promise,咱们能够写一个方法,统一处理
            resolvePromise(promise2, x, resolve, reject); //下一次then的实例promise2,此次返回值x,promise2的成功方法,promise2的失败方法
          } catch (e) {
            reject(e)
          }
        }, 0);
      }
      if (this.status === 'rejected') {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e)
          }
        }, 0)
      }
      if (this.status === 'pending') {
        this.onResovleCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFufilled(this.value)
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0)
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason)
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0)
        })
      }
    });
    return promise2;
  }
}
module.exports = Promise
复制代码

测试

以上,咱们基本完成了一个本身的promise库。 最后,看看这个库可不可行,那么就须要测试。官方给出了一个测试的库promises-aplus-tests,它会帮咱们校验,这个库是否可行。另外测试须要用defer,它是promise的语法糖。

Promise.defer = Promise.deferred = function () {
  let dfd = {}
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
}
复制代码

安装

npm install -g promises-aplus-tests
复制代码

执行

promises-aplus-tests ./myPromise.js 
复制代码

以上,咱们就本身完成了一个基于Promise A+规范的Promise。

相关文章
相关标签/搜索