成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

實(shí)現(xiàn)Promise

xcc3641 / 2464人閱讀

摘要:使用是極好的,它是如此有用以至于我覺得應(yīng)該好好研究一下,甚至是實(shí)現(xiàn)一個(gè)簡易的版本。構(gòu)造函數(shù)檢查參數(shù)例如是不是函數(shù)啊初始化,創(chuàng)建對(duì)象執(zhí)行因此構(gòu)造函數(shù)里面?zhèn)魅氲氖橇⒓幢粓?zhí)行的。

使用Promise是極好的,它是如此有用以至于我覺得應(yīng)該好好研究一下Promise,甚至是實(shí)現(xiàn)一個(gè)簡易的版本。實(shí)現(xiàn)之前,我們先來看看Promise的用途:

使用Promise callback hell

Promise的第一個(gè)用途是能夠很好地解決回調(diào)黑洞的問題,假設(shè)要實(shí)現(xiàn)一個(gè)用戶展示的任務(wù),這個(gè)任務(wù)分為三步:

獲取用戶信息

獲取用戶圖像

彈窗提示

不使用Promise,我們的實(shí)現(xiàn)可能是這樣子:

getUserInfo(id, function (info) {
  getUserImage(info.img, function () {
    showTip();
  })
})

這里只是三步,如果有更長串的任務(wù)時(shí),我們就會(huì)陷入到回調(diào)黑洞之中,為了解決這個(gè)問題,我們就可以使用Promise來處理這一長串任務(wù),使用Promise的版本是這樣子的:

// getUserInfo返回promise
getUserInfo(id)
  .then(getUserImage)
  .then(showTip)
  .catch(function (e) {
     console.log(e);
  });

原來向右發(fā)展的代碼,開始向下發(fā)展,這樣也更適合編程習(xí)慣,如果要讓我們的代碼更加健壯,我們就需要在每一步來處理錯(cuò)誤信息,使用promise這后,我們只需要在最后的catch中做善后處理。

并發(fā)

假如我們要顯示某一個(gè)頁的10條記錄,但是我們只有一個(gè)通過id獲取記錄的接口,這樣我們就需要發(fā)送10個(gè)請(qǐng)求,并且所有請(qǐng)求都完成之后再將記錄全部添加到頁面之中,Promise在這個(gè)場(chǎng)景下使用是特別合適的。

代碼可能是這樣子:

// ids要獲取信息的所有記錄id
// getRecordById獲取記錄的接口,返回promise
Promise.all(ids.map(getRecordById))
  .then(showRecords)
  .catch(function (e) {
     console.log(e);
  });

這就是Promise的一些簡單的用途,當(dāng)然令人興奮的是Promise已經(jīng)是ES6的標(biāo)準(zhǔn),而且目前很多瀏覽器已經(jīng)原生支持Promise了。對(duì)于那些無法使用Promise的瀏覽器,我們就只能自己去實(shí)現(xiàn)了,下面就來看看Promise的簡單實(shí)現(xiàn)吧。

實(shí)現(xiàn) warm up

先來盜用一張MDN的圖,先來熱熱身,看看Promise的狀態(tài)遷移:

Promise有三種狀態(tài):

pending:初始狀態(tài), 非 fulfilled 或 rejected

fulfilled: 成功的操作

rejected: 失敗的操作

我們可以看出新建的Promise是pending狀態(tài),fulfill之后就會(huì)執(zhí)行調(diào)用then的回調(diào)函數(shù)了,倘若reject了就會(huì)調(diào)用catch來進(jìn)行異常處理了,并且無論是調(diào)用then還是catch都會(huì)返回新的promise,這就是為什么promise可以鏈?zhǔn)秸{(diào)用了。

接著,我們來研究一下規(guī)范是怎么描述
promise的。這里只抽取核心部分,邊界問題不考慮。

構(gòu)造函數(shù):Promise ( executor )

檢查參數(shù):例如executor是不是函數(shù)啊

初始化:[[State]]=pending,[[FulfillReactions]]=[],[[RejectReactions]]=[]

創(chuàng)建resolve對(duì)象:{[[Resolve]]: resolve, [[Reject]]: reject}

執(zhí)行executor:executor(resolve, reject)

因此構(gòu)造函數(shù)里面?zhèn)魅氲膃xcuter是立即被執(zhí)行的。FulfillReactions存儲(chǔ)著promise執(zhí)行成功時(shí)要做的操作,RejectReactions存儲(chǔ)著promise是要執(zhí)行的操作。

function Promise(resolver) {
  this._id = counter++;
  this._state = PENDING;
  this._result = undefined;
  this._subscribers = [];

  var promise = this;

  if (noop !== resolver) {
    try {
      resolver(function (value) {
        resolve(promise, value);
      }, function (reason) {
        reject(promise, reason);
      });
    } catch (e) {
      reject(promise, e);
    }
  }
}
FulfillPromise(promise, value)

檢查[[state]],必須為pending(不是pending的表示已經(jīng)解析,不能重復(fù)解析)

賦值:[[Result]]=value[[state]]=fulfilled

觸發(fā)[[FulfillReactions]]的操作

和FulfillPromise聯(lián)系最緊密的就是ResolvePromise了,這里我們給出的是ResolvePromise的實(shí)現(xiàn),區(qū)別只是多了直接解析Promise。

function resolve(promise, value) {
  // 要resolve的為promise(then的callback返回的是promise)
  if (typeof value === "object"
    && promise.constructor === value.constructor) {
    handleOwnThenable(promise, value);
  } 
  // 要resolve的是值
  else {
    if (promise._state !== PENDING) { return; }

    promise._result = value;
    promise._state = FULFILLED;

    asap(publish, promise);
  }
}

function handleOwnThenable(promise, thenable) {
  // 如果返回的promise已經(jīng)完成
  // 直接用該promise的值resolve父promise
  if (thenable._state === FULFILLED) {
    resolve(promise, thenable._result);
  } else if (thenable._state === REJECTED) {
    reject(promise, thenable._result);
  }
  // 如果返回的promise未完成
  // 要等該promise完成再resolve父promise
  else {
    subscribe(thenable, undefined, function(value) {
      resolve(promise, value);
    }, function(reason) {
      reject(promise, reason);
    });
  }
}
RejectPromise(promise, reason)

檢查[[state]],必須為pending(不是pending的表示已經(jīng)解析,不能重復(fù)解析)

賦值:[[Result]]=reason,[[state]]=rejected

觸發(fā)[[RejectReactions]]的操作

觸發(fā)[[FulfillReactions]]和觸發(fā)[[RejectReactions]]實(shí)際就是遍歷數(shù)組,執(zhí)行所有的回調(diào)函數(shù)。

function reject(promise, reason) {
  if (promise._state !== PENDING) { return; }

  promise._state = REJECTED;
  promise._result = reason;

  asap(publish, promise);
}
Promise.prototype.then(onFullfilled, onRejected)

promise=this

新建resultCapability三元組,{[[Promise]], [[Resolve]], [[Reject]]}([[Promise]]新建的)

fulfillReaction={[[Capabilities]]: resultCapability, [[Handler]]: onFulfilled}

rejectReaction={[[Capabilities]]: resultCapability, [[Handler]]: onRejected}

如果[[state]]是pending:fulfillReaction加入[[FulfillReactions]],rejectReaction加入[[RejectReactions]]

如果[[state]]是fulfilled:fulfillReaction加入執(zhí)行隊(duì)列

如果[[state]]是rejected:rejectReaction加入執(zhí)行隊(duì)列

返回resultCapability.[[Promise]]

這里可以看出構(gòu)造函數(shù)和then的關(guān)系是很緊密的,新建的promise如果是異步操作,那么狀態(tài)就是pending,調(diào)用then時(shí)會(huì)新建子promise,并且將回調(diào)操作加入父promise的[[FulfillReactions]]或[[RejectReactions]]的數(shù)組里,這實(shí)際就是發(fā)布訂閱模式

他們是這樣的關(guān)系:

無論是new promise還是調(diào)用then或catch,都會(huì)得到一個(gè)新的promise,這些promise都會(huì)訂閱父級(jí)promise的完成事件,父級(jí)promise完成之后就會(huì)執(zhí)行一系列的回調(diào)操作,也就是發(fā)布。

Promise.prototype.catch(onRejected)

then的語法糖:then(null, onRejected)

下面就是Promise原型:

Promise.prototype = {
  constructor: Promise,

  then: function (onFulfillment, onRejection) {
    var parent = this;
    var state = parent._state;

    if (state === FULFILLED && !onFulfillment
        || state === REJECTED && !onRejection) {
        return this;
    }

    var child = new Promise(noop);
    var result = parent._result;

    if (state) {
      var callback = arguments[state - 1];
      asap(function () {
        invokeCallback(state, child, callback, result);
      });
    } else {
      subscribe(parent, child, onFulfillment, onRejection);
    }

    return child;
  },

  "catch": function (onRejection) {
    return this.then(null, onRejection);
  }
};
Promise.resolve(value)

新建promise

調(diào)用ResolvePromise(promise, value)(未列出,會(huì)判斷一些情況然后調(diào)用FulfillPromise)

返回promise

Promise.resolve = function (arg) {
  var child = new Promise(noop);
  resolve(child, arg);
  return child;
};
Promise.reject(value)

新建promise

調(diào)用RejectPromise(promise, value)

返回promise

Promise.reject = function (reason) {
  var child = new Promise(noop);
  reject(child, reason);
  return child;
};
Promise.all(iterator)

到這里我們已經(jīng)能夠?qū)崿F(xiàn)基本的promise了,Promise.allPromise.race就不繼續(xù)描述了,有興趣的可以繼續(xù)去讀規(guī)范,這里上圖來說明我對(duì)這兩個(gè)函數(shù)的理解:

調(diào)用promise.all會(huì)新建一個(gè)對(duì)象來存儲(chǔ)所有promise的處理狀態(tài),保存執(zhí)行的結(jié)果,當(dāng)remain為0時(shí),就可以resolve 新建的promise,這樣就可以繼續(xù)往后執(zhí)行了。

Promise.all = function (promises) {
  var child = new Promise(noop);
  var record = {
    remain: promises.length,
    values: []
  };
  promises.forEach(function (promise, i) {
    if (promise._state === PENDING) {
      subscribe(promise, undefined, onFulfilled(i), onRejected);
    } else if (promise._state === REJECTED) {
      reject(child, promise._result);
      return false;
    } else {
      --record.remain;
      record.values[i] = promise._result;
      if (record.remain == 0) {
        resolve(child, values);
      }
   }
  });
  return child;

  function onFulfilled(i) {
    return function (val) {
      --record.remain;
      record.values[i] = val;
      if (record.remian === 0) {
        resolve(child, record.values);
      }
    }
  }
  
  function onRejected(reason) {
    reject(child, reason);
  }
};
Promise.race(iterator)

promise.race與promise.all類似,不過只要有一個(gè)promise完成了,我們就可以resolve新建的promise了。

Promise.race = function (promises) {
  var child = new Promise(noop);

  promises.forEach(function (promise, i) {
    if (promise._state === PENDING) {
      subscribe(promise, undefined, onFulfilled, onRejected);
    } else if (promise._state === REJECTED) {
      reject(child, promise._result);
      return false;
    } else {
      resolve(child, promise._result);
      return false;
    }
  });
  return child;

  function onFulfilled(val) {
    resolve(child, val);
  }
        
  function onRejected(reason) {
    reject(child, reason);
  }
};

這就是promise的基本內(nèi)容了,完整代碼請(qǐng)戳這里。

其他問題 promises 穿透

如果傳入then里面的參數(shù)不是函數(shù),就會(huì)被忽略,這就是promise穿透的原因,所以永遠(yuǎn)往then里面?zhèn)鬟f函數(shù)。答案可以從then方法里面調(diào)用的一個(gè)關(guān)鍵函數(shù)invokeCallback中找到答案:

function invokeCallback(settled, promise, callback, detail) {
    var hasCallback = (typeof callback === "function"),
      value, error, succeeded, failed;

    if (hasCallback) {

      try {
        value = callback(detail);
      } catch (e) {
        value = {
          error: e
        };
      }

      if (value && !!value.error) {
        failed = true;
        error = value.error;
        value = null;
      } else {
        succeeded = true;
      }

    }
    // then的參數(shù)不是函數(shù)
    // 會(huì)被忽略,也就是promise穿透
    else {
      value = detail;
      succeeded = true;
    }

    if (promise._state === PENDING) {
      if (hasCallback && succeeded
          || settled === FULFILLED) {
        resolve(promise, value);
      } else if (failed || settled === REJECTED) {
        reject(promise, error);
      }
    }
  }

例如如下例子,結(jié)果都是輸出foo:

Promise.resolve("foo").then(Promise.resolve("bar")).then(function (result) {
  console.log(result);
});

Promise.resolve("foo").then(null).then(function (result) {
  console.log(result);
});
擁抱金字塔

promise能夠很好的解決金字塔問題,但是有時(shí)候我們也是需要適當(dāng)使用金字塔的,例如我們要同時(shí)獲取兩個(gè)promise的結(jié)果,但是這兩個(gè)promise是有關(guān)聯(lián)的,也就是有順序的,該怎么辦?

也許解決方案會(huì)是這樣,定義一個(gè)全局變量,這樣在第二個(gè)then里面就可以使用兩個(gè)promise的結(jié)果了。

var user;
getUserByName("nolan").then(function (result) {
  user = result;
  return getUserAccountById(user.id);
}).then(function (userAccount) {
  // 好了, "user" 和 "userAccount" 都有了
});

但是這不是最好的方案,此時(shí)何不拋棄成見,擁抱金字塔:

getUserByName("nolan").then(function (user) {
  return getUserAccountById(user.id).then(function (userAccount) {
    // 好了, "user" 和 "userAccount" 都有了
  });
});

promise是如此強(qiáng)大而且難以理解,但是抓住實(shí)質(zhì)之后其實(shí)并沒有想象的那么復(fù)雜,這也是為什么我要寫下這篇文章。更過關(guān)于如何正確使用promise,請(qǐng)看第三篇參考文章,強(qiáng)力推薦。

參考

實(shí)現(xiàn)promise

MDN-Promise

ES6-Promise規(guī)范

We have a problem with promises

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/85939.html

相關(guān)文章

  • 異步發(fā)展流程 —— 手寫一個(gè)符合 Promise/A+ 規(guī)范的 Promise

    摘要:構(gòu)造函數(shù)的實(shí)現(xiàn)我們?cè)谑褂玫臅r(shí)候其實(shí)是使用關(guān)鍵字創(chuàng)建了一個(gè)的實(shí)例,其實(shí)是一個(gè)類,即構(gòu)造函數(shù),下面來實(shí)現(xiàn)構(gòu)造函數(shù)。 showImg(https://segmentfault.com/img/remote/1460000018998456); 閱讀原文 概述 Promise 是 js 異步編程的一種解決方案,避免了 回調(diào)地獄 給編程帶來的麻煩,在 ES6 中成為了標(biāo)準(zhǔn),這篇文章重點(diǎn)不是敘...

    UnixAgain 評(píng)論0 收藏0
  • 實(shí)現(xiàn) JavaScript 異步方法 Promise.all

    摘要:本次的任務(wù)假如。。。。。引擎發(fā)生了重大故障,方法變成了,為了拯救世界,需要開發(fā)一個(gè)模塊來解決此問題。實(shí)現(xiàn)首先要知道是什么是對(duì)異步編程的一種抽象。數(shù)組中任何一個(gè)為的話,則整個(gè)調(diào)用會(huì)立即終止,并返回一個(gè)的新的對(duì)象。 本次的任務(wù) 假如。。。。。 JavaScript v8 引擎發(fā)生了重大故障,Promise.all 方法變成了 undefined ,為了拯救 JavaScript 世界,需要...

    mushang 評(píng)論0 收藏0
  • 從0到1實(shí)現(xiàn)Promise

    摘要:通過或者拿到方法回調(diào)函數(shù)的返回值,然后調(diào)用,將新增的的和傳入到中。打印結(jié)果實(shí)現(xiàn)方法接收一個(gè)包含多個(gè)的數(shù)組,當(dāng)有一個(gè)為狀態(tài)時(shí),整個(gè)大的為,并執(zhí)行回調(diào)函數(shù)。 前言 Promise大家一定都不陌生了,JavaScript異步流程從最初的Callback,到Promise,到Generator,再到目前使用最多的Async/Await(如果對(duì)于這些不熟悉的可以參考我另一篇文章《JavaScri...

    EddieChan 評(píng)論0 收藏0
  • promise介紹--實(shí)現(xiàn)

    摘要:內(nèi)部總體上分為兩種情況,一種是當(dāng)前對(duì)象狀態(tài)已經(jīng)變?yōu)榛颍藭r(shí)則直接把響應(yīng)的回調(diào)函數(shù)添加到異步隊(duì)列中,另一種情況是當(dāng)前對(duì)象狀態(tài)還是,此時(shí)則把響應(yīng)的回調(diào)函數(shù)依次添加到數(shù)組中。 今天,我?guī)е蠹乙徊揭徊礁?guī)范實(shí)現(xiàn)一個(gè)自己的Promise,大家可以對(duì)照我的第二篇文章Promise介紹--規(guī)范篇或官方規(guī)范來一一學(xué)習(xí)。 Promise內(nèi)部有三個(gè)固定的狀態(tài),我們?cè)谖募刑崆岸x。 const PEN...

    shery 評(píng)論0 收藏0
  • promise/A+規(guī)范翻譯以及手寫實(shí)現(xiàn)

    摘要:如果實(shí)現(xiàn)滿足所有要求,則實(shí)現(xiàn)可能允許。本條款允許使用特定于實(shí)現(xiàn)的方法來采用已知一致承諾的狀態(tài)。接下來根據(jù)規(guī)范進(jìn)行手寫實(shí)現(xiàn)注釋偷懶就將對(duì)應(yīng)的規(guī)范標(biāo)注出來,其實(shí)基本上就是對(duì)著規(guī)范實(shí)現(xiàn)。 如果要手寫實(shí)現(xiàn)promise,那么先看看promise/A+規(guī)范,再來實(shí)現(xiàn),將會(huì)事半功倍。那么我先翻譯一下Promise/A+規(guī)范中的內(nèi)容。 術(shù)語 1.1 promise 是一個(gè)帶有符合此規(guī)范的the...

    LiuZh 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<