【原】手写一个promise 手把手教你实现一个完整的 Promise

上一篇文章中,咱们介绍了Promise的基本使用,在这篇文章中,咱们试着本身来写一个Promise,主要是学习Promise的内部机制,学习它的编程思想。javascript

!!!备注:本文写的很差,仅供本身学习之用,具体的实现过程建议看下面的参考文章。因此本文没有发布到博客园首页和其余地方html

 

Promise API分析

正常使用方法

咱们来看一个正常的使用:java

var p=new Promise(function(resolve,rejcet){
    setTimeout(function(){
        if(true){
            resolve('success');
        }else{
            rejcet('failure');
        }
    },1000);
});
p.then(function(value){
    console.log(value);
},function(error){
    console.log(error);
});
//success

接下来咱们就来实现这么一个Promise.git

 

先来了解相关的一些术语: es6

    解决(fulfill):指一个 promise 成功时进行的一系列操做,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后世的 promise 实现多以 resolve 来指代之。
    拒绝(reject):指一个 promise 失败时进行的一系列操做。
    终值(eventual value):所谓终值,指的是 promise 被解决时传递给解决回调的值,因为 promise 有一次性的特征,所以当这个值被传递时,标志着 promise 等待态的结束,故称之终值,有时也直接简称为值(value)。
    据因(reason):也就是拒绝缘由,指在 promise 被拒绝时传递给拒绝回调的值。github

 

Promise的流程图分析

promise的执行流程如以下:编程

Promise链式操做中,执行顺序是如何保证的

    每一个promise后面链一个对象该对象包含onfulfiled,onrejected,子promise三个属性,当父promise 状态改变完毕,执行完相应的onfulfiled/onfulfiled的时候呢,拿到子promise,在等待这个子promise状态改变,再执行相应的onfulfiled/onfulfiled。依次循环直到当前promise没有子promisesegmentfault

 

如何让异步的value在thenable函数中拿到

    将resolve/reject函数和onfulfiled/onrejected放入同一个对象(promise对象)里面,resolve/reject的时候将value设置this.value=xxx。onfulfiled/onrejected执行的时候呢,onfulfiled(this.value)便可。数组

   在这里避免头晕,解释一下,onfulfilled和onrejected指的是then里面的两个函数。promise

 

状态机制切换

     如图所示,状态只能由pengding-->fulfilled,或者由pending-->rejected这样转变。

     只要这两种状况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会当即获得这个结果。这与事件(Event)彻底不一样,事件的特色是,若是你错过了它,再去监听,是得不到结果的。

 

开始手写Promise

一、 首先咱们来写好咱们的框架

ES6原生的构建方式为:

// Promise构造函数接收一个executor函数,executor函数执行完同步或异步操做后,调用它的两个参数resolve和reject
var promise = new Promise(function(resolve, reject) {
  /*
    若是操做成功,调用resolve并传入value
    若是操做失败,调用reject并传入reason
  */
})

咱们就按照这种方式来搭好框架

function Promise(callback) {
  var self = this
  self.status = 'PENDING' // Promise当前的状态
  self.data = undefined  // Promise的值
  self.onResolvedCallback = [] // Promise resolve时的回调函数集
  self.onRejectedCallback = [] // Promise reject时的回调函数集
  callback(resolve, reject) // 执行executor并传入相应的参数

  function resolve(value){

  }
  function rejecte(error){

  }
}
// 添加咱们的then方法
Promise.prototype.then=function(){

}

咱们构造一个Promise函数,并传入一个回调callback,callback里面传入两个函数做为参数,一个是resove,一个是reject。并在Promise的原型上加入咱们的then方法。

 

二、完善框架里面的内容 

框架搭好了,接下来咱们来一点点的完善框架里面的内容,能够这么说,把resolve,reject和then补充完,基本能够说就是把Promise完成了。

咱们先来完善咱们的resolve和rejected:

function Promise(callback) {
  var self = this
  self.status = 'PENDING' // Promise当前的状态
  self.data = undefined  // Promise的值
  self.onResolvedCallback = [] // Promise resolve时的回调函数集
  self.onRejectedCallback = [] // Promise reject时的回调函数集
  callback(resolve, reject) // 执行executor并传入相应的参数

  function resolve(value){
      if(self.status=='PENDING'){
          self.status=='FULFILLED';
          self.data=value;
          // 依次执行成功以后的函数栈
          for(var i = 0; i < self.onResolvedCallback.length; i++) {
            self.onResolvedCallback[i](value)
          }
      }
  }

  function rejecte(error){
    if (self.status === 'PENDING') {
       self.status = 'REJECTED'
       self.data = error;
       // 依次执行失败以后的函数栈
       for(var i = 0; i < self.onRejectedCallback.length; i++) {
           self.onRejectedCallback[i](error)
        }
    }
  }
}

若是是penging,则改变相应的状态,并把resolve和reject的值保存子data里面。

 

接下来咱们实现咱们的then方法:

then方法是Promise的核心,所以这里会花比较大的篇幅去介绍then:

 一个promise的then接受两个参数:

promise.then(onFulfilled, onRejected)

onFulfilled 和 onRejected 都是可选参数。

  • 若是 onFulfilled 不是函数,其必须被忽略
  • 若是 onRejected 不是函数,其必须被忽略

onFulfilled 特性

若是 onFulfilled 是函数:

  • 当 promise 执行结束后其必须被调用,其第一个参数为 promise 的终值,也就是resolve传过来的值
  • 在 promise 执行结束前其不可被调用
  • 其调用次数不可超过一次

onRejected 特性

若是 onRejected 是函数:

  • 当 promise 被拒绝执行后其必须被调用,其第一个参数为 promise 的据因,也就是reject传过来的值
  • 在 promise 被拒绝执行前其不可被调用
  • 其调用次数不可超过一次

调用时机

onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用(平台代码指引擎、环境以及 promise 的实施代码)

调用要求

onFulfilled 和 onRejected 必须被做为函数调用(即没有 this 值,在 严格模式(strict) 中,函数 this 的值为 undefined ;在非严格模式中其为全局对象。)

屡次调用

then 方法能够被同一个 promise 调用屡次

  • 当 promise 成功执行时,全部 onFulfilled 需按照其注册顺序依次回调
  • 当 promise 被拒绝执行时,全部的 onRejected 需按照其注册顺序依次回调

返回

then 方法必须返回一个 promise 对象 

promise2 = promise1.then(onFulfilled, onRejected);
  • 若是 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程[[Resolve]](promise2, x)
  • 若是 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
  • 若是 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
  • 若是 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因

不论 promise1 被 reject 仍是被 resolve 时 promise2 都会被 resolve,只有出现异常时才会被 rejected

   每一个Promise对象均可以在其上屡次调用then方法,而每次调用then返回的Promise的状态取决于那一次调用then时传入参数的返回值,因此then不能返回this,由于then每次返回的Promise的结果都有可能不一样。

 

接下来咱们来写咱们的then方法:

Promise.prototype.then = function(onResolved, onRejected) {
  var self = this
  var promise2

  // 根据标准,若是then的参数不是function,则咱们须要忽略它,此处以以下方式处理
  onResolved = typeof onResolved === 'function' ? onResolved : function(value) {}
  onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {}

  if (self.status === 'resolved') {
    // 若是promise1(此处即为this/self)的状态已经肯定而且是resolved,咱们调用onResolved
    // 由于考虑到有可能throw,因此咱们将其包在try/catch块里
    return promise2 = new Promise(function(resolve, reject) {
      try {
        var x = onResolved(self.data)
        if (x instanceof Promise) { // 若是onResolved的返回值是一个Promise对象,直接取它的结果作为promise2的结果
          x.then(resolve, reject)
        }
        resolve(x) // 不然,以它的返回值作为promise2的结果
      } catch (e) {
        reject(e) // 若是出错,以捕获到的错误作为promise2的结果
      }
    })
  }

  // 此处与前一个if块的逻辑几乎相同,区别在于所调用的是onRejected函数,就再也不作过多解释
  if (self.status === 'rejected') {
    return promise2 = new Promise(function(resolve, reject) {
      try {
        var x = onRejected(self.data)
        if (x instanceof Promise) {
          x.then(resolve, reject)
        }
      } catch (e) {
        reject(e)
      }
    })
  }

  if (self.status === 'pending') {
  // 若是当前的Promise还处于pending状态,咱们并不能肯定调用onResolved仍是onRejected,
  // 只能等到Promise的状态肯定后,才能确实如何处理。
  // 因此咱们须要把咱们的**两种状况**的处理逻辑作为callback放入promise1(此处即this/self)的回调数组里
  // 逻辑自己跟第一个if块内的几乎一致,此处不作过多解释
    return promise2 = new Promise(function(resolve, reject) {
      self.onResolvedCallback.push(function(value) {
        try {
          var x = onResolved(self.data)
          if (x instanceof Promise) {
            x.then(resolve, reject)
          }
        } catch (e) {
          reject(e)
        }
      })

      self.onRejectedCallback.push(function(reason) {
        try {
          var x = onRejected(self.data)
          if (x instanceof Promise) {
            x.then(resolve, reject)
          }
        } catch (e) {
          reject(e)
        }
      })
    })
  }
}

// 为了下文方便,咱们顺便实现一个catch方法
Promise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected)
}

 

完整代码以下:

try {
  module.exports = Promise
} catch (e) {}
  
function Promise(executor) {
  var self = this
  
  self.status = 'pending'
  self.onResolvedCallback = []
  self.onRejectedCallback = []
  
  function resolve(value) {
    if (value instanceof Promise) {
      return value.then(resolve, reject)
    }
    setTimeout(function() { // 异步执行全部的回调函数
      if (self.status === 'pending') {
        self.status = 'resolved'
        self.data = value
        for (var i = 0; i < self.onResolvedCallback.length; i++) {
          self.onResolvedCallback[i](value)
        }
      }
    })
  }
  
  function reject(reason) {
    setTimeout(function() { // 异步执行全部的回调函数
      if (self.status === 'pending') {
        self.status = 'rejected'
        self.data = reason
        for (var i = 0; i < self.onRejectedCallback.length; i++) {
          self.onRejectedCallback[i](reason)
        }
      }
    })
  }
  
  try {
    executor(resolve, reject)
  } catch (reason) {
    reject(reason)
  }
}
  
function resolvePromise(promise2, x, resolve, reject) {
  var then
  var thenCalledOrThrow = false
  
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise!'))
  }
  
  if (x instanceof Promise) {
    if (x.status === 'pending') { //because x could resolved by a Promise Object
      x.then(function(v) {
        resolvePromise(promise2, v, resolve, reject)
      }, reject)
    } else { //but if it is resolved, it will never resolved by a Promise Object but a static value;
      x.then(resolve, reject)
    }
    return
  }
  
  if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) {
    try {
      then = x.then //because x.then could be a getter
      if (typeof then === 'function') {
        then.call(x, function rs(y) {
          if (thenCalledOrThrow) return
          thenCalledOrThrow = true
          return resolvePromise(promise2, y, resolve, reject)
        }, function rj(r) {
          if (thenCalledOrThrow) return
          thenCalledOrThrow = true
          return reject(r)
        })
      } else {
        resolve(x)
      }
    } catch (e) {
      if (thenCalledOrThrow) return
      thenCalledOrThrow = true
      return reject(e)
    }
  } else {
    resolve(x)
  }
}
  
Promise.prototype.then = function(onResolved, onRejected) {
  var self = this
  var promise2
  onResolved = typeof onResolved === 'function' ? onResolved : function(v) {
    return v
  }
  onRejected = typeof onRejected === 'function' ? onRejected : function(r) {
    throw r
  }
  
  if (self.status === 'resolved') {
    return promise2 = new Promise(function(resolve, reject) {
      setTimeout(function() { // 异步执行onResolved
        try {
          var x = onResolved(self.data)
          resolvePromise(promise2, x, resolve, reject)
        } catch (reason) {
          reject(reason)
        }
      })
    })
  }
  
  if (self.status === 'rejected') {
    return promise2 = new Promise(function(resolve, reject) {
      setTimeout(function() { // 异步执行onRejected
        try {
          var x = onRejected(self.data)
          resolvePromise(promise2, x, resolve, reject)
        } catch (reason) {
          reject(reason)
        }
      })
    })
  }
  
  if (self.status === 'pending') {
    // 这里之因此没有异步执行,是由于这些函数必然会被resolve或reject调用,而resolve或reject函数里的内容已经是异步执行,构造函数里的定义
    return promise2 = new Promise(function(resolve, reject) {
      self.onResolvedCallback.push(function(value) {
        try {
          var x = onResolved(value)
          resolvePromise(promise2, x, resolve, reject)
        } catch (r) {
          reject(r)
        }
      })
  
      self.onRejectedCallback.push(function(reason) {
          try {
            var x = onRejected(reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (r) {
            reject(r)
          }
        })
    })
  }
}
  
Promise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected)
}
  
Promise.deferred = Promise.defer = function() {
  var dfd = {}
  dfd.promise = new Promise(function(resolve, reject) {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}
View Code

 

  

  

本文没有写好,有点头晕,因此具体的实现过程仍是建议看下面的参考文章。 

 

参考:

大白话promise

史上最易读懂的 Promise 实现

谈谈 ES6 的 Promise 对象

手把手教你实现一个完整的 Promise

Promise简单实现(正常思路版)

promise源码分析

【翻译】Promises/A+规范

相关文章
相关标签/搜索