手寫一個PromiseA+的完成

Promise

手寫一個PromiseA+的完成。注重這裏只是模仿,實際上原生的promise在事件行列中屬於microTask。這裏用setTimeout模仿不是迥殊適當。由於setTimeout是一個macroTask。

1. 最簡樸的基本功能

/**
 * 定義Promise
 * 先完成一個最簡樸的。用setTimeout模仿一個異步的要求。
 */
function Promise(fn){
  var value= null;
  var callbacks = [];
  this.then = function(onFulfilled) {
    callbacks.push(onFulfilled);
  }

  function resolve(value){
    callbacks.forEach(function(cb){
      cb(value);
    })
  }

  fn(resolve);
}


// 運用Promise
var p = new Promise(function(resolve){
  setTimeout(function(){
    resolve('這是相應的數據')
  },2000)
})

p.then(function(response){
  console.log(response);
})

2.鏈式挪用

/**
 * 先看一下前一個例子存在的題目
 * 1.在前一個例子中不停挪用then須要支撐鏈式挪用,每次實行then都要返回挪用對象自身。
 * 2.在前一個例子中,當鏈式挪用的時刻,每次then中的值都是同一個值,這是有題目的。實在第一次then中的返回值,應當是第二次挪用then中的函數的參數,順次類推。
 * 所以,我們進一步優化一下代碼。
 * 
 */
function Promise(fn){
  var value= null;
  var callbacks = [];
  this.then = function(onFulfilled) {
    callbacks.push({f:onFulfilled});
    return this;
  }

  function resolve(value){
    callbacks.map(function(cb,index){
      if(index === 0){
        callbacks[index].value = value;
      }
      var rsp = cb.f(cb.value);
      if(typeof callbacks[index+1] !== 'undefined'){
        callbacks[index+1].value = rsp;
      }
    })
  }
  fn(resolve);
}


// 運用Promise
var p = new Promise(function(resolve){
  setTimeout(function(){
    resolve('這是相應的數據')
  },2000)
})

p.then(function(response){
  console.log(response);
  return 1;
}).then(function(response){
  console.log(response);
  return 2;  
}).then(function(response){
  console.log(response);
})

3. 異步

/**
 * 先看一下前一個例子存在的題目
 * 1. 假如在then要領註冊回調之前,resolve函數就實行了,怎麼辦?比方 new Promise的時刻傳入的函數是同步函數的話,
 * then還沒被註冊,resolve就實行了。。這在PromiseA+範例中是不允許的,範例明確要求回調須要經由過程異步的體式格局實行。
 * 用來保證一致牢靠的實行遞次。
 * 
 * 因而我們須要到場一些處置懲罰。把resolve里的代碼放到異步行列中去。這裏我們應用setTimeout來完成。
 * 道理就是經由過程setTimeout機制,將resolve中實行回調的邏輯安排到JS使命行列末端,以保證在resolve實行時,
 * then要領的回調函數已註冊完成
 * 
 */
function Promise(fn){
  var value= null;
  var callbacks = [];
  this.then = function(onFulfilled) {
    callbacks.push({f:onFulfilled});
    return this;
  }

  function resolve(value){
    setTimeout(function(){
        callbacks.map(function(cb,index){
          if(index === 0){
            callbacks[index].value = value;
          }
          var rsp = cb.f(cb.value);
          if(typeof callbacks[index+1] !== 'undefined'){
            callbacks[index+1].value = rsp;
          }
        })
    },0)
  }
  fn(resolve);
}


// 運用Promise,如今即使是同步的立馬resolve,也能一般運轉了。
var p = new Promise(function(resolve){
    resolve('這是相應的數據')
})

p.then(function(response){
  console.log(response);
  return 1;
}).then(function(response){
  console.log(response);
  return 2;  
}).then(function(response){
  console.log(response);
})

4. 狀況機制

/**
 * 先看一下前一個例子存在的題目
 * 1.前一個例子還存在一些題目,假如Promise異步操縱已勝利,在這之前註冊的一切回調都邑實行,
 * 然則在這以後再註冊的回調函數就不再實行了。詳細的運轉下面這段代碼,能夠看到“can i invoke”並沒有打印出來
 * 想要處理這個題目,我們就須要到場狀況機制了。詳細完成看本文件夾下的另一個js文件里的代碼。
 * 
 */
function Promise(fn){
  var value= null;
  var callbacks = [];
  this.then = function(onFulfilled) {
    callbacks.push({f:onFulfilled});
    return this;
  }

  function resolve(value){
    setTimeout(function(){
        callbacks.map(function(cb,index){
          if(index === 0){
            callbacks[index].value = value;
          }
          var rsp = cb.f(cb.value);
          if(typeof callbacks[index+1] !== 'undefined'){
            callbacks[index+1].value = rsp;
          }
        })
    },0)
  }
  fn(resolve);
}


// 
var p = new Promise(function(resolve){
    resolve('這是相應的數據')
})

p.then(function(response){
  console.log(response);
  return 1;
}).then(function(response){
  console.log(response);
  return 2;  
}).then(function(response){
  console.log(response);
})

setTimeout(function(){
   p.then(function(response){
     console.log('can i invoke?');
   })
},0)
/**
 * 在promise01.js中,我們已剖析了,我們須要到場狀況機制
 * 在這裏完成一下PromiseA+中關於狀況的範例。
 * 
 * Promises/A+範例中的2.1Promise States中明確規定了,pending能夠轉化為fulfilled或rejected而且只能轉化一次,
 * 也就是說假如pending轉化到fulfilled狀況,那末就不能再轉化到rejected。
 * 而且fulfilled和rejected狀況只能由pending轉化而來,兩者之間不能相互轉換
 * 
 */
function Promise(fn){
  var status = 'pending'
  var value= null;
  var callbacks = [];
  this.then = function(onFulfilled) {
    // 假如是pending狀況,則到場到註冊行列中去。
    if(status === 'pending'){
      callbacks.push({f:onFulfilled});
      return this;
    }
    // 假如是fulfilled 狀況,此時直接實行傳入的註冊函數即可。
    onFulfilled(value);
    return this;
  }

  function resolve(newValue){
    value = newValue;
    status = 'fulfilled';
    setTimeout(function(){
        callbacks.map(function(cb,index){
          if(index === 0){
            callbacks[index].value = newValue;
          }
          var rsp = cb.f(cb.value);
          if(typeof callbacks[index+1] !== 'undefined'){
            callbacks[index+1].value = rsp;
          }
        })
    },0)
  }
  fn(resolve);
}


// 
var p = new Promise(function(resolve){
    resolve('這是相應的數據')
})

p.then(function(response){
  console.log(response);
  return 1;
}).then(function(response){
  console.log(response);
  return 2;  
}).then(function(response){
  console.log(response);
})

setTimeout(function(){
   p.then(function(response){
     console.log('can i invoke?');
   })
},1000)
/**
 * 適才的例子中,確切打印出了 can i invoke,然則之前then的註冊函數的返回值,並沒有打印出來。
 * 也就是說 1 和 2 並沒有被打印出來,看下面的解釋
 * 
 */
function Promise(fn){
  var status = 'pending'
  var value= null;
  var callbacks = [];
  this.then = function(onFulfilled) {

    if(status === 'pending'){
      callbacks.push({f:onFulfilled});
      return this;
    }

    onFulfilled(value);
    return this;
  }

  function resolve(newValue){
    value = newValue;
    status = 'fulfilled';
    setTimeout(function(){
        callbacks.map(function(cb,index){
          if(index === 0){
            callbacks[index].value = newValue;
          }
          var rsp = cb.f(cb.value);
          if(typeof callbacks[index+1] !== 'undefined'){
            callbacks[index+1].value = rsp;
          }
        })
    },0)
  }
  fn(resolve);
}

var p = new Promise(function(resolve){
    resolve('aaaaaa')
})

p.then(function(response){
  console.log(response);    
  return 1;
}).then(function(response){
  console.log(response);  // 這裏應當打印的是45行返回的1,然則打印出來的確是aaaaaa
  return 2;  
}).then(function(response){
  console.log(response); // 這裏應當打印的是48行返回的2,然則打印出來的確是aaaaaa
})

setTimeout(function(){
   p.then(function(response){
     console.log('can i invoke?');
   })
},1000)


/**
 * 題目的泉源在於什麼呢?
 * 題目的泉源是每次的then的返回值都是p,當狀況是fulfilled,實行的是onFulfilled(value)
 * 此處的value是p的value,也就是fulfilled狀況的value。依據範例,promise應當是只能發射單值。
 * 而我們設想了一個callback客棧中有一系列的值。生生的把promise變成了多值發射。
 * 
 * 所以,調解思緒,每一個then都應當返回一個promise,這個promise應當是一個全新的promise。
 * 詳細完成見下一個例子。
 */
/**
 * 依據適才的剖析,我們從新優化一下代碼
 * 1.去掉之前的多值設想
 * 2.每次的then 返回的都是一個全新的promise
 *
 */
function Promise(fn){
  var status = 'pending'
  var value= null;
  var callbacks = [];
  var self = this;

  this.then = function(onFulfilled) {
    return new Promise(function(resolve){
      function handle(value){
        var res = typeof onFulfilled === 'function' ? onFulfilled(value) : value;
        resolve(res);
      }
      // 假如是pending狀況,則到場到註冊行列中去。
      if(status === 'pending'){
        callbacks.push(handle);
      // 假如是fulfilled 狀況。
      }else if(status === 'fulfilled'){
          handle(value);
      }
    })
  }

  function resolve(newValue){
    value = newValue;
    status = 'fulfilled';
    
    setTimeout(function(){
        callbacks.map(function(cb){
          cb(value);
        })
    },0)
  };

  fn(resolve);
}


// 
var p = new Promise(function(resolve){
    resolve('這是相應的數據')
})

p.then(function(response){
  console.log(response);
  return 1;
}).then(function(response){
  console.log(response);
  return 2;  
}).then(function(response){
  console.log(response);
})

setTimeout(function(){
   p.then(function(response){
     console.log('can i invoke?');
   })
},1000)



/**
 * 運轉一下,圓滿輸出
 * 先是輸出“這是相應的數據”,然後是“1”,然後是“2”, 然後是“can i invoke?”
 * 
 * 接下來我們要好好整頓一下代碼了。把一些公用的要領放到組織函數的原型上去。革新以後的例子見下一個例子
 */
/**
 * 依據適才的剖析,我們從新優化一下代碼
 * 1.把私有屬性掛到實例上去
 * 2.把大眾要領掛到組織函數的原型上去
 *
 */
function Promise(fn){
  this.status = 'pending';
  this.value= null;
  this.callbacks = [];
  var self = this;
  function resolve(newValue){
    self.value = newValue;
    self.status = 'fulfilled';
    setTimeout(function(){
      self.callbacks.map(function(cb){
          cb(value);
        })
    },0)
  }
  fn(resolve);
}


Promise.prototype = Object.create(null);
Promise.prototype.constructor = Promise;


Promise.prototype.then = function(onFulfilled){
  var self = this;
  return new Promise(function(resolve){
    function handle(value){
      var res = typeof onFulfilled === 'function'?  onFulfilled(value) : value;
      resolve(res);
    }
    if(self.status==='pending'){
      self.callbacks.push(handle);
    }else if(self.status ==='fulfilled'){
      handle(self.value);
    }
  })
}

// 運用
var p = new Promise(function(resolve){
    resolve('這是相應的數據')
})

p.then(function(response){
  console.log(response);
  return 1;
}).then(function(response){
  console.log(response);
  return 2;  
}).then(function(response){
  console.log(response);
})

setTimeout(function(){
   p.then(function(response){
     console.log('can i invoke?');
   })
},1000)

5.處置懲罰註冊的函數返回值是promise的狀況

/**
 * 不出預料,又要拋出題目了。當then註冊的回調函數返回的是promise的時刻,從這個then以後的一切then的註冊函數
 * 都應當註冊在新返回的promise上。直到碰到下一個回調函數的返回值也是promise。
 * 
 * 完成思緒:
 * 在handle中推斷註冊函數返回的是不是是promise。假如是的話,則resolve這個返回的promise的值,詳細代碼看一下36到38行
 * 
 */
function Promise(fn){
  this.status = 'pending';
  this.value= null;
  this.callbacks = [];
  var self = this;
  function resolve(newValue){
    self.value = newValue;
    self.status = 'fulfilled';
    setTimeout(function(){
      self.callbacks.map(function(cb){
          cb(value);
        })
    },0)
  }
  fn(resolve);
}


Promise.prototype = Object.create(null);
Promise.prototype.constructor = Promise;


Promise.prototype.then = function(onFulfilled){
  var self = this;
  var promise = new Promise(function(resolve){
    function handle(value){
      var res = typeof onFulfilled === 'function'?  onFulfilled(value) : value;
      if(res instanceof Promise){
        promise = res;
        resolve(res.value);
      }else {
        resolve(res);
      }
    }
    if(self.status==='pending'){
      self.callbacks.push(handle);
    }else if(self.status ==='fulfilled'){
      handle(self.value);
    }
  })
  return promise;
}

// 運用
var p = new Promise(function(resolve){
    resolve('這是相應的數據')
})

p.then(function(response){
  console.log(response);
  return new Promise(function(resolve){
    resolve('testtest')
  })
}).then(function(response){
  console.log(response);
  return 2;  
}).then(function(response){
  console.log(response);
})

setTimeout(function(){
   p.then(function(response){
     console.log('can i invoke?');
     return new Promise(function(resolve){
        resolve('hhhhhh')
      })
   }).then(function(response){
     console.log(response);
   })
},1000)

源碼悉數在github上:https://github.com/JesseZhao1…

    原文作者:jessezhao1990
    原文地址: https://segmentfault.com/a/1190000014994499
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞