天天看點

[class,SimplePromise,Promise,函數,回調函數]JavaScript之實作一個ES6Promise

  說到 ES6,Promise 是繞不過的問題;如果說 ES6 的 Class 是基于 Javascript 原型繼承的封裝,那麼 Promise 則是對 callback 回調機制的改進。這篇文章,不談 Promise 的實際應用;聊一下 Promise 的實作原理,從最簡單的解決方案入手,一步一步的自己實作一個 SimplePromise。

  正文

  從最簡單的 Promise 初始化和使用入手:

  const pro=new Promise ((res, rej)=> {})

  pro.then(data=> {}, err=> {})

  Promise 的構造函數如上,需要傳遞一個函數作為參數,這個函數有兩個變量: resolve, reject。而 Promise 有不同的執行狀态,分三種情況:Resolve, Reject, Pending。根據以上的資訊,寫出最基本的 SimplePromise 的類結構:

  class SimplePromise{

  constructor(handler){

  this._status="PENDING"

  handler(this._resolve.bind(this), this._reject.bind(this))//參數函數的作用域指向Class

  }

  _resolve(){}

  _reject(){}

  接下來思考一下_resolve 與_reject兩個函數的作用。我們知道,Promise 根據 then 方法來執行回調,而 then 是根據狀态判斷要執行的回調函數。不難推導出,_resolve 與_reject正是根據handler的執行來進行狀态變更的,而狀态隻能由Pending向Reslove或Rejected轉換,是以有:

  ...

  _resolve(val){//異步傳回的資料

  if(this._status==="PENDING"){//保證狀态的不可逆性

  this._status="RESOLVED"

  this._value=val

  _reject(val){

  if(this._status==="PENDING"){

  this._status="REJECTED"

  then的調用邏輯

  下面分析 then 函數的邏輯,從調用入手:

  then 接收兩個參數,第一個是執行成功調用的函數,第二個是執行失敗調用的函數。

  _resolve(val){

  then(success, fail){

  switch (this._status){

  case "PENDING":

  break;

  case "RESOLVED":

  success(this._value)

  case "REJECTED":

  fail(this._value)

  以上實作了最簡單的一個 Promise

  測試代碼:

  const pro=new SimplePromise(function(res, rej) {

  let random=Math.random() * 10

  if(random > 5){

  res("success")

  else{

  rej("fail")

  })

  pro.then(function(data) {

  console.log(data)

  }, function(err) {

  console.log(err)

  當然,這不能算是一個 Promise,目前僅僅實作了根據狀态調用不同的回調函數。還沒有實作異步。

  那如何實作異步呢?關鍵在于 then 函數,當判斷_status為PENDING時,如何延後調用 success與fail函數,等待狀态改變後再調用?

  支援異步

  這裡采用數組來存儲 fail 與 success 函數:

  this.status="PENDING"

  this._onSuccess=[]//存儲fail 與 success 函數

  this._onFail=[]

  handler(this._resolve.bind(this), this._reject.bind(this))

  if(this.status==="PENDING"){

  let temp

  while(this._onSuccess.length > 0){//依次執行onSuccess中的回調函數

  temp=this._onSuccess.shift()

  temp(val)

  while(this._onFail.length > 0){

  temp=this._onFail.shift()

  then (success, fail){

  switch (this.status){

  this._onSuccess.push(success)

  this._onFail.push(fail)

  使用 onSuccess 和 onFail 來存儲回調函數,當處理狀态為 PENDING 時,将回調函數 push 到相應的數組裡,當狀态變更後,依次執行數組裡的回調函數。

  setTimeout(function(){

  }, 2000)

  兩秒後,會執行相應的回調。

  到目前為止,最最最簡單的一個 Promise 骨架已經基本完成了。但是還有很多功能待完成。現在可以稍微休息一下,喝個咖啡打個雞血,回來我們會繼續讓這個 Promise 骨架更加豐滿起來。

  . . . . . .

  完善Promise

  歡迎回來,下面我們繼續完善我們的 Promise。

  上面完成了一個最基礎的 Promise,然而還遠遠不夠。首先,Promise 需要實作

二手域名購買平台

鍊式調用,其次 Promise 還需要實作 all race resolve reject 等靜态函數。

  首先,如何實作 then 的鍊式調用呢?需要 then 傳回的也是一個 Promise。

  于是有

  return new SimplePromise((nextSuccess, nextFail)=> {

  const onFullfil=function(val){

  const res=success(val)

  nextSuccess(res)

  const onReject=function(val){

  const res=fail(val)

  nextSuccess(res) ;

  this._onSuccess.push(onFullfil)

  this._onFail.push(onReject)

  onFullfil(this._value)

  onReject(this._value)

  const sp=new SimplePromise(function (res, rej){

  random > 5 res(random) : rej(random)

  }, 1000)

  sp.then(data=> {

  console.log("more than 5 " + data)

  return data

  }, err=>{

  console.log("less than 5 " + err)

  return err

  }).then((data)=> {

  then的參數限制

  完成了鍊式調用,then 方法還有許多其他限制:

  下面思考 以下問題:代碼中四個使用 promise 的語句之間的不同點在哪兒?

  假設 doSomething 也 doSomethingElse 都傳回 Promise

  doSomething().then(function () {

  return doSomethingElse();

  }).then(finalHandler);

  doSomethingElse();

  }).then(finalHandler);;

  doSomething().then(doSomethingElse()).then(finalHandler);;

  doSomething().then(doSomethingElse).then(finalHandler);;

  答案 一會兒再揭曉,我們先來梳理以下then 方法對傳入不同類型參數的處理機制:

  直接上代碼:

  if(typeof success !=="function"){

  nextSuccess(val)

  const res=success(val)//success 的傳回值

  if(res instanceof SimplePromise){//如果success 傳回一個promise 對象

  res.then(nextSuccess, nextFail)

  if(fail){

  if(typeof fail !=="function"){

  if(res instanceof SimplePromise){

  onReject=function(){}

  對于傳入 then 方法的參數,首先判斷其是否為 function,判斷為否,直接執行 下一個 then 的 success 函數;判斷為是,接着判斷函數的傳回值 res 類型是否為 Promise,如果為否,直接執行下一個 then 的 success 函數,如果為是,通過 then 調用接下來的函數。

  是以,上面的問題就不難得到答案了。

  

  return doSomethingElse();//傳回值為Promise

  RETURN:

  doSomething

  --->doSomethingElse(undefined)

  ---> final(doSomethingElseResult)

  doSomethingElse();//傳回值為 undefined

  ---> final(undefined)

  doSomething().then(doSomethingElse())//參數 typeof !=function

  .then(finalHandler);

  doSomethingElse(undefined)

  ---> final(doSomethingResult)

  doSomething().then(doSomethingElse)//與1的調用方式是不同的

  --->doSomethingElse(doSomethingResult)

  好,then 方法已經完善好了。

  靜态函數

  接下來是 Promise 的各種靜态函數

  class SimplePromise(){

  static all(){}

  static race(){}

  static resolve(){}

  static reject(){}

  all

  static all(promiselist){

  if(Array.isArray(promiselist)){

  const len=promiselist.length;

  const count=0

  const arr=[]

  return new SimplePromise((res, rej)=> {

  for(let i=0; i {

  arr[i]=data

  count ++

  if(count===len){//每一個Promise都執行完畢後傳回

  res(arr)

  }, err=> {

  rej(err)

  race

  static race(promiselist){

  const len=promiselist.length

  return new SimplePromise((res, rej)=>{

  promiselist.forEach(item=>{

  this.resolve(item).then(data=> {

  res(data)

  resolve

  static resolve(obj){

  if(obj instanceof SimplePromise){

  return obj

  else {

  return new SimplePromise((res)=>{

  res(obj)

  reject

  static reject(obj){

  rej(obj)

  總結

  現在,一個完整的 Promise 對象就完成了。現在來總結一下 callback 回調和 Promise 的異同吧。

  其實,不管是 callback 還是 Promise,這二者都是将需要滞後執行方法而提前聲明的方式,隻不過 callback 的處理方式比較粗犷,将 cb 函數放到異步執行的結尾;而 Promise 優于 cb 的是通過定義了不同的執行狀态,更加細緻的進行結果處理,提供了很好的 catch 機制,這是其一;其二,then 的鍊式調用解決了 cb 的回調地獄;但是 then 的鍊式調用也不是很好的解決方案,如果封裝不好,then裡面套用大量的代碼的話也會引起代碼的不美觀和閱讀上的困難,這一方面的終極解決方法還是 es7 的 async/await。

繼續閱讀