一、背景

promiz是一个Promise的一个库,体积较小,官方介绍大概913 bytes,里面具体介绍了promise是如何实现的,主要是以ES5的一些语法来实现的

二、代码浅析

很多类库都以这种方式来写,都写在一个闭包里(比如jQuery)
好处:把promiz中的方法和变量保护起来,为了防止全局变量污染;
劣处:它会产生不释放的栈内存,过多使用容易导致内存溢出或者降低性能
下面是promiz的大致结构

(function(){
  global = this
  // 变量声明
  var queueId = 1
  var queue = {}
  var isRunningTask = false
  //异步执行器
  function nextTick(){
  //  xxxx
  }
  //构造函数
  function Deferred(resolver){
  // 变量
  // 方法:resolve、reject、then、catch
  // 一些工具方法:thennable、fire
  }
 
  //对外暴露API
  if (typeof module != 'undefined') {
    module['exports'] = Deferred
  } else {
    global['Promise'] = global['Promise'] || Deferred
  }
})()

具体看下这些方法是怎么实现的呢?
1、异步执行器
说起异步执行,我们通常想到的大概就这四个吧,但使用场景各不同吧

  • setTimeOut()
  • setInterval()
  • setImmediate()
  • process.nextTik()

前三个可以归为宏任务,第四个为微任务,微任务的执行优先级是高于宏任务的,在promiz中的异步执行具体实现如下

if (!global.setImmediate)
global.addEventListener('message', function (e) {
  if (e.source == global){
    if (isRunningTask)
      nextTick(queue[e.data])
    else {
      isRunningTask = true
      try {
        queue[e.data]()
      } catch (e) {}

      delete queue[e.data]
      isRunningTask = false
    }
  }
})
  function nextTick(fn) {
  if (global.setImmediate) setImmediate(fn)
    // if inside of web worker
    else if (global.importScripts) setTimeout(fn)
    else {
      queueId++
      queue[queueId] = fn
      global.postMessage(queueId, '*')
    }
  }

下面是构造函数
首先进来会有类型判断,判断传入的resolver是否是函数并且不是undefined,resolve和reject两个方法中均调用异步执行,在调用之前会有一个判断,看到当前状态是否改变,如果改变了就不执行了,没改变就执行,这也就是promise中的pending状态只能变成成功或者失败状态后,该状态就不可改变了;
then方法中 d = new Deferred(),然后返回d,就是每次then方法执行都会返回一个新的promise,then中会判断当前状态是3时就直接执行resolve方法,当前状态是4时就直接执行reject方法

function Deferred(resolver){
'use strict'
    if (typeof resolver != 'function' && resolver != undefined)
      throw TypeError()

    if (typeof this != 'object' || (this && this.then))
      throw TypeError()

    // states
    // 0: pending
    // 1: resolving
    // 2: rejecting
    // 3: resolved
    // 4: rejected
    var self = this,
      state = 0, // promise状态
      val = 0,   // success callback返回值
      next = [], // 返回的新的promise对象
      fn, er;    // then方法中的成功回调函数和失败回调函数

    self['promise'] = self

    self['resolve'] = function (v) {
      fn = self.fn
      er = self.er
      if (!state) {
        val = v
        state = 1

        nextTick(fire)
      }
      return self
    }

    self['reject'] = function (v) {
      fn = self.fn
      er = self.er
      if (!state) {
        val = v
        state = 2

        nextTick(fire)

      }
      return self
    }

    self['_d'] = 1

    self['then'] = function (_fn, _er) {
      if (!(this._d == 1))
        throw TypeError()

      var d = new Deferred()

      d.fn = _fn
      d.er = _er
      if (state == 3) {
        d.resolve(val)
      }
      else if (state == 4) {
        d.reject(val)
      }
      else {
        next.push(d)
      }

      return d
    }

    self['catch'] = function (_er) {
      return self['then'](null, _er)
    }
}

在上面的构造函数中我们看到了改变状态会异步触发一个fire函数,下面看一下fire函数是干什么的

function fire() {

      // check if it's a thenable
      var ref;
      try {
        ref = val && val.then
      } catch (e) {
        val = e
        state = 2
        return fire()
      }

      thennable(ref, function () {
        state = 1
        fire()
      }, function () {
        state = 2
        fire()
      }, function () {
        try {
          if (state == 1 && typeof fn == 'function') {
            val = fn(val)
          }

          else if (state == 2 && typeof er == 'function') {
            val = er(val)
            state = 1
          }
        } catch (e) {
          val = e
          return finish()
        }

        if (val == self) {
          val = TypeError()
          finish()
        } else thennable(ref, function () {
            finish(3)
          }, finish, function () {
            finish(state == 1 && 3)
          })

      })
    }

fire中判断成功时callback的返回值是否是一个promise,并且执行thennable函数,然后传递了3个回调函数,那么接下来看下thennable是干啥的呢

    // ref : reference to 'then' function
    // cb, ec, cn : successCallback, failureCallback, notThennableCallback
    function thennable (ref, cb, ec, cn) {
      // Promises can be rejected with other promises, which should pass through
      if (state == 2) {
        return cn()
      }
      if ((typeof val == 'object' || typeof val == 'function') && typeof ref == 'function') {
        try {

          // cnt protects against abuse calls from spec checker
          var cnt = 0
          ref.call(val, function (v) {
            if (cnt++) return
            val = v
            cb()
          }, function (v) {
            if (cnt++) return
            val = v
            ec()
          })
        } catch (e) {
          val = e
          ec()
        }
      } else {
        cn()
      }
    };

在thenable中,会先判断当前状态是2就执行cn;如果ref不是一个函数时同样也会执行cn;

三、总结

promiz库代码量少,但实现方式算是独特吧,短小精悍


辛格
11 声望0 粉丝