JavaScript之Promise实现

ES2015提出了Promise,同时基于Promise的异步开发将开发者中回调地狱中解救出来。但在没有原生支持的环境下,需要借助Promise/A+之类的库来实现Promise,今天就来尝试自行实现Promise。

1 基本实现

首先来完成一个Promise类的基本框架:

function Promise(fn) {
  var resolveCallback = null
  var rejectCallback = null
  
  this.then = function(onResolved, onRejected) {
      resolveCallback = onResolved
      rejectCallback = onRejected
  }
  
  this.resolve = function(value) {
      this.resolveCallback(value)
  }
  
  this.reject = function(reason) {
      this.rejectCallback(reason)
  }
  
  fn(this.resolve, this.reject)
}

以上便是Promise的基本实现。

2 状态管理

上述的代码存在一个问题,resolve方法会调用多次,所以接下来我们需要接入状态管理。

Promise内部存在3个状态:

  • pending
  • resolved
  • rejected

接下来在现有代码之上,加入状态管理:

function MyPromise(fn) {
  let state = 'pending'
  var resolveCallback = null
  var rejectCallback = null
  var childResolve
  var childReject
  
  this.then = function(onResolved, onRejected) {
      resolveCallback = onResolved
      rejectCallback = onRejected
  }
  
  this.resolve = function(value) {
      if(state === 'pending') {
         this.resolveCallback(value)
         state = 'resolved' 
      } 
  }
  
  this.reject = function(reason) {
      if(state === 'pending') {
          this.rejectCallback(reason)
          state = 'rejected'
      }   
  }
  
  fn(this.resolve, this.reject)
}

3 链式调用

上述Promise实现可以完成正常的异步调用,但是却无法实现链式回调,原因在于其then方法没有返回一个新的Promise对象,所以接下来还需要改造then方法,实现链式调用:

  this.then = function(onResolved, onRejected) {
      if(state === 'pending') {
          resolveCallback = onResolved
          rejectCallback = onRejected
      }
    
      return new MyPromise((resolve, reject) => {
        ......
      })
  }

光返回一个promise对象还没用,接下来我们来写个demo测试下:

  var demo = new MyPromise((resolve, reject) => {
      setTimeout(() => {
          resolve('my first promise')
      }, 1000)
  })

  demo.then((msg) => {
      console.log(msg)
      return 'my second promise'
  }).then((msg) => {
      console.log(msg)
  })

其输出为:

my first promise

事实上,第二个promise对象的resolve reject方法从未被调用过,因而其onResolved onRejected的回调ye就无从调用。所以还必须指定时机调用字promise对象的resolvereject

所以首先需要在创建新promise对象时,记录其resolvereject方法:

function MyPromise() {
    ......
    var childResolve
    var childReject
    
      this.then = function(onResolved, onRejected) {
      if(state === 'pending') {
          resolveCallback = onResolved
          rejectCallback = onRejected
      }
    
      return new MyPromise((resolve, reject) => {
        childResolve = resolve
        childReject = reject
      })
  }
}

接下来还需在resolvereject方法中调用子对象的resolvereject方法,整个Promise完整代码如下:

function MyPromise(fn) {
    let state = 'pending'
    var resolveCallback = null
    var rejectCallback = null
    var childResolve = null
    var childReject = null
    
    this.then = function(onResolved, onRejected) {
        if(state === 'pending') {
            resolveCallback = onResolved
            rejectCallback = onRejected
        }
      
        return new MyPromise((resolve, reject) => {
            childResolve = resolve
            childReject = reject
        })
    }
    
    this.resolve = function(value) {
        if(state === 'pending') {
            if(resolveCallback) {
                var ret = resolveCallback(value)
                childResolve(ret)
                state = 'resolved' 
            }

        } 
    }
    
    this.reject = function(reason) {
        if(state === 'pending') {
            if(rejectCallback) {
                var ret = rejectCallback(reason)
                childReject(ret)
                state = 'rejected'
            }
        }   
    }
    
    fn(this.resolve, this.reject)
  }
上一篇:使用Spark Streaming SQL基于时间窗口进行数据统计


下一篇:RecyclerView用法和源码深度解析