头图

在了解实现之前最好知道promise的使用,不会的话可以先去查看相关文档。除此之外,还需要了解事件循环,了解宏任务与微任务。

实现

该Promise实现只考虑resolve,reject同resolve的思路差不多就不参与进来添加阅读成本了。

// 使用示例:
new Promise1(exec).then(fullfill).then(fullfill2)

function exec(resolve) {
  console.log(1)
  setTimeout(() => {
    resolve(1)
  },3000)
}

function fullfill(val) {
  console.log(val,1)
  return new Promise1((resolve) => {
    setTimeout(() => {
      resolve(2)
    },3000)
  })
}

function fullfill2(val) {
  console.log(val,2)
}

我们先不考虑内部具体干了什么,根据示例可以知道Promise与then分别接收了一个exec与fullfill两个外部传来的函数,而resolve,与then一定是在结构内部的,因此可以首先大致推出一个Promise的结构

function Promise1() {
  function resolve(){}
  this.then = function() {
  }
}

在使用Promise的时候,可以发现在使用过程中通过Promise接收到的exec函数是同步执行的,且exec接收了resolve这个函数,而另一个fullfill则接受了一个val参数并且自身作为then的参数,所以可以进一步得到下面的情况

function Promise1(exec) {
  function resolve() {}
  this.then = function(fullfill(val)) {
  }
  exec(resolve)
}

现在我们来梳理一下promise流程顺序,先是执行了内部的exec函数并以resolve为参传入,然后通过resolve触发then函数中传入的fullfill函数。

可以发现是在then函数执行的时候先将传入的fullfill函数储存起来,然后等到resolve触发时再将储存的fullfill函数取出并执行,而在这个时候也正好能将传入resolve函数的val参数传给fullfilledFunc使用。

所以我们需要一个数组将fullfill函数在then执行的时候存起来,并且在resolve中取出调用,如下

function Promise1(exec) {
  let fullfillStack = []
  function resolve(val) {
    fullfillStack.forEach((fullfill) => {
      fullfilledFunc(val)  
    })
  }
  this.then = function(fullfill) {
      fullfillStack.push(fullfill)
  }
  exec(resolve)
}

以上代码明显都是同步进行的,但是resolve肯定只能在then函数调用之后,否则fullfillStack中可能什么都没有就执行resolve了。所以我们需要一个计时器来实现异步延迟的效果。

function Promise1(exec) {
  let fullfillStack = []
  function resolve(val) {
    setTimeOut(() => {
      fullfillStack.forEach((fullfill) => {
        fullfill(val)  
      })    
    },0)
  }
  this.then = function(fullfill) {
      fullfillStack.push(fullfill)
  }
  exec(resolve)
}

我们知道Promise是加入了微任务的,原生Promise在浏览器中是通过MutationObserve这个API实现的,而在Node中则是通过process.nextTick实现的,我们先采用Node(因为简单哈哈),浏览器版本在最后。

function Promise1(exec) {
  let fullfillStack = []
  function resolve(val) {
    process.nextTick(() => {
      fullfillStack.forEach((fullfill) => {
        fullfill(val)  
      })   
    })
  }
  this.then = function(fullfill) {
      fullfillStack.push(fullfill)
  }
  exec(resolve)
}

我们知道在then执行之后返回的是一个promise,并且可以如示例中采用调用链的方式,此时就需要借助状态来确保对结果的处理以及调用链的实现,见下:

function Promise1(exec) {
  let fullfillStack = []
  let state = 'pending'
  let data = null
  function resolve(val) {
    process.nextTick(() => {
      state = 'fullfilled'
      data = val
      fullfillStack.forEach((fullfill) => {
        fullfill(data)  
      })
    })
  }
  this.then = function(fullfill) {
    if(state === 'pending') {
      fullfillStack.push(fullfill)
      return this
    } else if (state === 'fullfilled'){
      fullfill(data)
    }
  }
  exec(resolve)
}

但是这样会发现示例中的结果为:

11

12

第二个then中的val仍是第一个then中的val,这是因为每次都返回了promise本身而不是一个新的promise,实际上这二者应该是毫无关系的,所以我们应该返回一个新的promise

function Promise1(exec) {
  let fullfillStack = []
  let state = 'pending'
  let data = null
  function resolve(val) {
    process.nextTick(() => {
      state = 'fullfilled'
      data = val
      fullfillStack.forEach((fullfill) => {
        fullfill(data)  
      })
    })
  }
  this.then = function(fullfill) {
    return new Promise1(resolve => {
      function success() {
        //对传入的fullfill判断并对data处理
        let result = typeof fullfill === 'function' ? fullfill(data) : data
        //判断result是否为promise
        if(result instanceof Promise1 && typeof result['then'] === 'function') {
          result.then(function(val){
            resolve(val)
          })  
        } else {
          resolve(result)
        }
      }  
      if(state === 'pending') {
        fullfillStack.push(success)
      } else if (state === 'fullfilled'){
        success(data)
      }
    })
  }
  exec(resolve)
}

由于要返回一个新的promise,所以我们直接在then函数里生成Promise实例,同时我们需要对then函数接收到的参数进行判断,所以我们用一个函数将这些处理封装起来。如果是一个函数,我们才能让它去对resolve传过来的值进行处理。对于处理完之后的结果,我们同样需要再进行处理,因为结果有可能是个promise。

其他的地方基本上就和上一步骤的差不多了,注意在状态为pending的时候,应该推入success而不是原来的fullfill了。

到这promise也就基本上实现了。

浏览器版实现:

function resolve(val) {
     //make microRequest
    let callback = function() {
        fullfilledStack.forEach((fullfilledFn) => {
        fullfilledFn(val)
    })
  let child = document.createElement('div')
  let config = { childList: true }
  let observer = new MutationObserver(callback)
  observer.observe(targetNode, config)
    
  let targetNode = document.createElement('div')
  function triggerCallback(targetNode, child) {
      targetNode.children.length > 0 ?     targetNode.removechild(child):targetNode.appendChild(child)
  }
  triggerCallback(targetNode, child)
}

NONO
6 声望0 粉丝

没心没肺丧尸一只