丁鹿学堂:前端面试分享之手写promise(完结)

56 阅读2分钟
promise返回对象的then方法

我们手写promise中then方法的实现。第一步应该检测then方法传过来的参数是不是一个函数,如果是函数,我们就把他添加到chain里面, 成功和失败的里面去添加一个回调。

代码如下:

  let chain = {
    then:function(){
      let args = arguments
      tuples.forEach((tuple,i)=>{
        let fn = toString.call(args[i] === '[object Function]') && args[i]
        chain[tuple[1]](function(){
          fn&&fn.apply(this,arguments)
        })
      })
    }
  } 

这里的fn其实就是then方法里面的参数函数,所以我们要把参数传递给fn去执行。

相当于说我们有两个容器,一个是成功的,一个是失败的。注意,我们的fn不是说添加到成功的回调,而是在成功的回调里去调用。失败的函数也是这个意思。

then方法返回值仍然应该是一个promise

这一步其实很简单,就是思维要适应。我们在then方法里再调用一次Promise方法就可以了。

但是代码也要做相应的改造处理,即如果返回的是一个promise,我们应该怎么做。

那应该怎么判断呢? 我们可以通过检测是否是一个对象,而且这个对象里面有没有then方法。

 let chain = {
    then:function(){
      let args = arguments
      return Promise(function(resolve,reject){
        tuples.forEach((tuple,i)=>{
          let fn = toString.call(args[i] === '[object Function]') && args[i]
          chain[tuple[1]](function(){
            let returned = fn&&fn.apply(this,arguments)
            if(returned && toString.call(returned.then === '[object Function]')){
              returned.done(resolve)
              returned.fail(reject)
            }
          })
        })
      })
    }

总结: 其实我们实现promise分了两步走,第一步先是实现了一个工具函数container, 第二步是自定义的Promise,借助container的一些方法去实现了js中的Promise。

let cache = {} // 缓存对象
let container = function(flags){
flags = typeof flags === 'string' ? (cache[flags] || createFlags(flags)):extend({},flags)
let carryOut = false
let stack = []
let memory = false
let stackLen,stackPoint = 0
let fire = function(data){
  let len = stack.length;
  let i = stackPoint || 0
  stackPoint = 0
  memory = flags.memory && data // flags.memory存在就把data 赋值给变量memory
  carryOut = true
  for(;i<len;i++){
    if(stack[i].apply(data[0],data[1]) === false && flags.stopOnFalse){
      break
    }
    
  }
}
let obj = {
  add:function(){
    (function(args){
      stackLen = stack.length
      Array.from(args).forEach(item=>{
        if(toString.call(item) === '[object Function]'){
          if(!obj.has(item)){
            stack.push(item)
          }
        }else{
          throw new Error('请正确传参数')
        }
      })
    })(arguments)
    if(memory){
      stackPoint = stackLen
      fire(memory)
    }
  },
  startBind(context,args){
    args = args || []
    args = [context,args]
    if(!flags.once || !carryOut){
      fire(args)
    }
  },
  startup(){
    obj.startBind(this,arguments)
  },
  has(fn){
    return stack.indexOf(fn) > -1
  }
}
return obj
}
// (flags.match(/\S+/g) 是根据空格去分割字符串为数组
function createFlags(flags){
let res = {}
let arr = flags.match(/\S+/g) || []
arr.forEach(item =>{
res[item] = true
})

return res
}
function extend(to,from){
for(let key in from){
to[key] = from[key]
}
return to
}

// 2

function Promise(func){
let tuples = [
  ["resolve",'done',container('once memory'),'resolved'],
  ["reject",'fail',container('once memory'),'rejected'],
]
let state = 'pending'
let protect = {}
let chain = {
  then:function(){
    let args = arguments
    return Promise(function(resolve,reject){
      tuples.forEach((tuple,i)=>{
        let fn = toString.call(args[i] === '[object Function]') && args[i]
        chain[tuple[1]](function(){
          let returned = fn&&fn.apply(this,arguments)
          if(returned && toString.call(returned.then === '[object Function]')){
            returned.done(resolve)
            returned.fail(reject)
          }
        })
      })
    })
  }
} //promise返回的是一个对象
tuples.forEach((tuple,i)=>{
  let list = tuple[2]
  let stateString = tuple[3]
  if(stateString){
    list.add(function(){
      state = stateString
    })
  }
  chain[tuple[1]] = list.add
  protect[tuple[0]] = function(){
    protect[tuple[0]+'Bind'](this,arguments)
  }
  protect[tuple[0]+'Bind'] = list.startBind
})
if(func){
  func.call(chain,protect['resolve'],protect['reject'])
}
return chain
}