我所理解的Promise

(嫌我話多的可以直接看分割線之后的部分…)
以前高中的時(shí)候自己搗騰博客,一直也就只會(huì)用JQuery寫(xiě)點(diǎn)按鈕事件什么的,連表單提交都沒(méi)寫(xiě)過(guò),后來(lái)誤打誤撞做了前端碼農(nóng)舊覺(jué)得JS的異步模式實(shí)在是太坑爹,當(dāng)你搞清楚異步回調(diào)的時(shí)候,又會(huì)發(fā)現(xiàn)回調(diào)地獄(Callback Hell)太坑爹…
為什么覺(jué)得異步坑爹?看看下面這個(gè)例子:

//以下用setTimeout()模擬一個(gè)請(qǐng)求

function getName() {
  return "小白妹妹";
}

function greeting(name) {
  console.log("你好," + name);
}

//生成一個(gè)0到1000的隨機(jī)數(shù),模擬不確定的等待時(shí)間0-1秒
var randomTime = function () {
  return Math.random() * 1000;
};

var name = "";

//1秒之內(nèi)給name賦值為 小白妹妹,但不知道具體時(shí)間
setTimeout(function () {
  name = getName();
}, randomTime());

greeting(name); //以為得到name之后就可以開(kāi)心的去打招呼啦,然而…

這是錯(cuò)的!這是錯(cuò)的!這是錯(cuò)的!

可能很多新手都犯過(guò)這個(gè)錯(cuò)誤,錯(cuò)的時(shí)候還不知道為啥錯(cuò)了…深究原因的話跟JS的機(jī)制有關(guān),長(zhǎng)篇大論的就不在這里多說(shuō)了(其實(shí)是我說(shuō)不清楚…)

再有點(diǎn)經(jīng)驗(yàn),就會(huì)知道,應(yīng)該把greeting(name)寫(xiě)在回調(diào)函數(shù)里,這樣就能保證在得到數(shù)據(jù)之后才運(yùn)行greeting()函數(shù),于是…當(dāng)你有多個(gè)請(qǐng)求并且之間是有這種依賴(lài)關(guān)系的時(shí)候,可怕的回調(diào)地獄就出現(xiàn)了!
而解決回調(diào)地獄其中一個(gè)很優(yōu)雅的方法,就是使用傳說(shuō)中的promise!

關(guān)于promise這個(gè)概念我前前后后看了有一年了,周?chē)矝](méi)有認(rèn)識(shí)的人可以給我講,只能自己看看網(wǎng)上的文章,然而…并沒(méi)有什么卵用…網(wǎng)上的文章都是眾說(shuō)紛紜,有一上來(lái)就defer的[1],也有一上來(lái)就說(shuō)如果你還在用defer你就理解錯(cuò)了Promise的[2],就所以我到現(xiàn)在也不知道究竟什么樣的理解才是對(duì)的…

關(guān)于Promise以及A+規(guī)范就不在此詳述了,那些概念的東東,我也還沒(méi)完全理解。我想做的是讓跟我一樣的小白都能明白Promise最基本的用法。


舉個(gè)栗子…
有一個(gè)第三方提供的API,訪問(wèn)API能夠得到一些用戶(hù)數(shù)據(jù)(每訪問(wèn)一次得到一頁(yè),假設(shè)由于某些限制一頁(yè)只返回3個(gè)用戶(hù))以及下一頁(yè)的index;除了第一頁(yè)之外,其他的頁(yè)面都需要下一頁(yè)的index參數(shù)才能訪問(wèn)到。(先不管這個(gè)API設(shè)計(jì)的合不合理…Facebook就有這樣的API)
http://example.com/user -->訪問(wèn)第一頁(yè)的用戶(hù)數(shù)據(jù)
http://example.com/user?next=xzmca -->訪問(wèn)第二頁(yè)的用戶(hù)數(shù)據(jù)
請(qǐng)求第一頁(yè)時(shí)返回結(jié)果如下:

{
"items" : [{
  "name" : "小白妹妹",
   "age" : 10
  }, 
{...}, {...}],
"nextPage" : "xzmca",
"lastPage" : null
}

你可能會(huì)有這樣的需求:你的APP一次需要顯示6個(gè)甚至更多個(gè)的用戶(hù)數(shù)據(jù)。而根據(jù)之前的API,一次只能拿到3個(gè)數(shù)據(jù),那么就只能發(fā)出兩次請(qǐng)求,并且第二次請(qǐng)求依賴(lài)于第一次請(qǐng)求的結(jié)果,由于異步的原因我們并不知道第一個(gè)請(qǐng)求什么時(shí)候才完成,而我最初入坑時(shí)是讓程序發(fā)完第一個(gè)請(qǐng)求后強(qiáng)制等2秒再發(fā)第二個(gè)請(qǐng)求我會(huì)告訴你們嗎…

下面為了方便,使用setTimeout()函數(shù)和bluebird庫(kù)進(jìn)行說(shuō)明:

//生成一個(gè)0到3000的隨機(jī)數(shù),模擬不確定的等待時(shí)間0-3秒
var randomTime = function () {
  return Math.random() * 3000;
};

//只考慮最簡(jiǎn)單的情況promise被resolve,暫時(shí)不考慮promise被reject的情況
function req1() {
  return new Promise(function (resolve) {

    //使用setTimeout()來(lái)模擬發(fā)送請(qǐng)求,data為請(qǐng)求得到的數(shù)據(jù)
    setTimeout(function () {
      var data = {
        "items": [{
          "name": "小白妹妹",
          "age": 10
        }, {
          "name": "小白",
          "age": 100
        }, {
          "name": "妹妹",
          "age": 111
        }],
        "nextPage": "asdfa",
        "lastPage": null
      };

      resolve(data);
      console.log("請(qǐng)求1完成");
    }, randomTime());
  })
}

function req2(dataFromReq1) {
  return new Promise(function (resolve) {

    setTimeout(function () {
      var data = {
        "items": [{
          "name": "小黑姐姐",
          "age": 20
        }, {
          "name": "小黑",
          "age": 233
        }, {
          "name": "姐姐",
          "age": 250
        }],
        "nextPage": "gwdfx",
        "lastPage": "asdfa"
      };
      console.log(dataFromReq1);
      var finalUserData = dataFromReq1.items.concat(data.items); //將兩次得到的用戶(hù)數(shù)據(jù)合并
      resolve(finalUserData);
      console.log("請(qǐng)求2完成");
    }, randomTime());
  })
}

//讓數(shù)據(jù)在promise鏈上歡快的傳遞吧~
req1().then(req2).then(function (data) {
  console.log(data);
});

Q: 什么時(shí)候需要返回一個(gè)promise呢?
A: 當(dāng)你的需求邏輯是,XXX的執(zhí)行需要依賴(lài)OOO的結(jié)果,此時(shí)OOO就應(yīng)該返回一個(gè)promise

Q: 為什么req1要打括號(hào),而req2不打括號(hào)?
A:這個(gè)我也沒(méi)太搞清楚XD,我的理解是,req1()打括號(hào)執(zhí)行后才會(huì)返回promise,不打括號(hào)就只是一個(gè)沒(méi)有執(zhí)行的函數(shù),req2不打括號(hào)是因?yàn)閠hen的入?yún)⒅荒苁且粋€(gè)函數(shù),如果打了括號(hào)執(zhí)行后就不是函數(shù)了。

Q: 最后一個(gè)then的function(data) data是哪里來(lái)的?
A: req2的定義中,有一句resolve(finalUserData),在Promise Chain中,每個(gè)then的入?yún)⒌娜雲(yún)⒁簿褪莊unction(data)中的data都是由前一個(gè)promise resolve時(shí)傳遞而來(lái)的


  1. https://github.com/alsotang/node-lessons/tree/master/lesson17 ?

  2. http://web.jobbole.com/82601/ ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容