Learn Promise

Promise是抽象異步處理對(duì)象以及對(duì)其進(jìn)行各種操作的組件爽雄。

API 三種類型:

  • Constructor
    從構(gòu)造函數(shù) Promise 來創(chuàng)建一個(gè)新建新 promise 對(duì)象作為接口。
    要想創(chuàng)建一個(gè)promise對(duì)象侥蒙、可以使用 new 來調(diào)用 Promise 的構(gòu)造器來進(jìn)行實(shí)例化

    var promise = new Promise((resolve, reject) => {
    // 異步處理
    // 處理結(jié)束后绳军、調(diào)用resolve 或 reject
    })
    
  • Instance Method
    對(duì)通過new生成的promise對(duì)象為了設(shè)置其值在 resolve(成功) / reject(失敗)時(shí)調(diào)用的回調(diào)函數(shù) 可以使用 promise.then() 實(shí)例方法

    • promise.then(onFulfilled, onRejected)
    • resolve(成功)時(shí), onFulfilled 會(huì)被調(diào)用
    • reject(失敗)時(shí), onRejected 會(huì)被調(diào)用
      只想對(duì)異常進(jìn)行處理時(shí)印机,更好的選擇是使用promise.catch(onRejected)
  • Static Method
    包括 Promise.all() 還有 Promise.resolve() 等在內(nèi),主要都是一些對(duì)Promise進(jìn)行操作的輔助方法门驾。

Promise workflow
示例代碼:

function asyncFunction() {
  // new Promise 構(gòu)造器之后射赛,會(huì)返回一個(gè)promise對(duì)象
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Async Hello world')
    }, 16)
  })
}

// asyncFunction這個(gè)函數(shù)會(huì)返回promise對(duì)象。對(duì)于這個(gè)promise對(duì)象奶是,我們調(diào)用它的then方法來設(shè)置resolve后的回調(diào)函數(shù)楣责, catch方法來設(shè)置發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)竣灌。
asyncFunction()
  .then(value => {
    console.log(value) // => 'Async Hello world'(在這種情況下 catch 的回調(diào)函數(shù)并不會(huì)被執(zhí)行(因?yàn)閜romise返回了resolve))
  })
  .catch(err => {
    console.log(err) // 如果運(yùn)行環(huán)境沒有提供 setTimeout 函數(shù)的話,那么上面代碼在執(zhí)行中就會(huì)產(chǎn)生異常秆麸,在catch 中設(shè)置的回調(diào)函數(shù)就會(huì)被執(zhí)行初嘹。
  })

Promise的狀態(tài)

用 new Promise 實(shí)例化的promise對(duì)象有以下三個(gè)狀態(tài):

  • "has-resolution" - Fulfilled
    resolve(成功)時(shí)。此時(shí)會(huì)調(diào)用 onFulfilled
  • "has-rejection" - Rejected
    reject(失敗)時(shí)沮趣。此時(shí)會(huì)調(diào)用 onRejected
  • "unresolved" - Pending
    既不是resolve也不是reject的狀態(tài)屯烦。也就是promise對(duì)象剛被創(chuàng)建后的初始化狀態(tài)等

promise對(duì)象的狀態(tài),從Pending轉(zhuǎn)換為Fulfilled或Rejected之后房铭, 這個(gè)promise對(duì)象的狀態(tài)就不會(huì)再發(fā)生任何變化漫贞。也就是說,Promise與Event等不同育叁,在 .then 后執(zhí)行的函數(shù)可以肯定地說只會(huì)被調(diào)用一次。

另外芍殖,F(xiàn)ulfilled和Rejected這兩個(gè)中的任一狀態(tài)都可以表示為Settled(不變的)豪嗽。
Settled: resolve(成功) 或 reject(失敗)。

創(chuàng)建promise對(duì)象的流程如下所示豌骏。

  1. new Promise(fn) 返回一個(gè)promise對(duì)象
  2. 在 fn 中指定異步等處理
    • 處理結(jié)果正常的話龟梦,調(diào)用 resolve(處理結(jié)果值)
    • 處理結(jié)果錯(cuò)誤的話,調(diào)用 reject(Error對(duì)象)

實(shí)例:用Promise來通過異步處理方式來獲取XMLHttpRequest(XHR)的數(shù)據(jù)窃躲。

function getURL(url) {
  return new Promise((resolve, reject) => {
    const req = new XMLHttpRequest()
    req.open('GET', url, true)
    req.onload = () => {
      if (req.status === 200) {
        resolve(req.responseText)
      } else {
        reject(new Error(req.statusText))
      }
    }
    req.onerror = () => {
      reject(new Error(req.statusText))
    }
    req.send()
  })
}

// 運(yùn)行
const url = 'https://www.google.com.hk'
getURL(url)
  .then(onFulfilled = value => {
    console.log(value)
  })
  .catch(onRejected = error => {
    console.error(error)
  })

// 其實(shí)计贰,.catch 只是 promise.then(undefined, onRejected) 的別名而已, 如下代碼也可以完成同樣的功能蒂窒。
getURL(url)
  .then(onFulfilled, onRejected)

Promise.resolve

  • new Promise的快捷方式
    靜態(tài)方法 Promise.resolve(value) 可以認(rèn)為是 new Promise() 方法的快捷方式躁倒。
比如 Promise.resolve(42) 可以認(rèn)為是以下代碼的語法糖。

new Promise(resolve => {
  resolve(42)
})

// 方法 Promise.resolve(value) 的返回值也是一個(gè)Promise對(duì)象洒琢,所以我們可以像下面那樣接著對(duì)其返回值進(jìn)行 .then 調(diào)用秧秉。
Promise.resolve(42)
  .then(value => {
    console.log(value)
  })
  • 將thenable對(duì)象轉(zhuǎn)換Promise對(duì)象
    這種機(jī)制要求thenable對(duì)象所擁有的 then 方法應(yīng)該和Promise所擁有的 then 方法具有同樣的功能和處理過程,在將thenable對(duì)象轉(zhuǎn)換為promise對(duì)象的時(shí)候衰抑,還會(huì)巧妙的利用thenable對(duì)象原來具有的 then 方法象迎。
    實(shí)例: jQuery.ajax(),它的返回值就是thenable的(這個(gè)對(duì)象具有 .then 方法)呛踊。

    $.ajax('/json/comment.json') // => 擁有 .then 方法的對(duì)象
    
    // 這個(gè)thenable的對(duì)象可以使用 Promise.resolve 來轉(zhuǎn)換為一個(gè)promise對(duì)象
    const promise = Promise.resolve($.ajax('/json/comment.json')) // => promise對(duì)象
    promise.then(value => {
      console.log(value)
    })
    

Promise.reject

Promise.reject(error) 是和 Promise.resolve(value) 類似的靜態(tài)方法砾淌,是 new Promise() 方法的快捷方式。

比如 Promise.reject(new Error('error')) 是以下代碼的語法糖谭网。

new Promise((resolve, reject) => {
  reject(new Error('error'))
})

// 這段代碼的功能是調(diào)用該P(yáng)romise對(duì)象通過then指定的 onRejected 函數(shù)汪厨,并將錯(cuò)誤(Error)對(duì)象傳遞給這個(gè) onRejected 函數(shù)。
Promise.reject(new Error('BOOM!'))
  .catch(err => {
    console.error(err)
  })

避免對(duì)異步回調(diào)函數(shù)進(jìn)行同步調(diào)用

實(shí)例:根據(jù)執(zhí)行時(shí)DOM是否已經(jīng)裝載完畢來決定是對(duì)回調(diào)函數(shù)進(jìn)行同步調(diào)用還是異步調(diào)用蜻底。

function onReady(fn) {
  const readyState = document.readyState
  if (readyState === 'interactive' || readyState === 'complete') {
    setTimeout(fn, 0)
  } else {
    window.addEventListener('DOMContentLoaded', fn)
  }
}

onReady(() => {
  console.log('DOM fully loaded and parsed')
})
console.log('==Starting==')

// 用Promise重寫
function onReadyPromise() {
  return new Promise((resolve, reject) => {
    const readyState = document.readyState
    if (readyState === 'interactive' || readyState === 'complete') {
      resolve()
    } else {
      window.addEventListener('DOMContentLoaded', resolve)
    }
  })
}

onReadyPromise()
  .then(() => {
    console.log('DOM fully loaded and parsed')
  })
console.log('==Starting==')

Promise chain

  • Promise#then
  • Promise#catch

promise chain 中如何傳遞參數(shù)
實(shí)例:如果 Task A 想給 Task B 傳遞一個(gè)參數(shù)骄崩,那就在 Task A 中 return 的返回值聘鳞,會(huì)在 Task B 執(zhí)行時(shí)傳給它。

function doubleUp(value) {
  return value * 2
}
function increment(value) {
  return value + 1
}
function output(value) {
  console.log(value) // => (1 + 1) * 2
}

const promise = Promise.resolve(1)
promise
  .then(increment) 
  .then(doubleUp)  
  .then(output) 
  .catch(err => {
    // promise chain中出現(xiàn)異常的時(shí)候會(huì)被調(diào)用
    console.error(err)
  })

return的值會(huì)由 Promise.resolve(return的返回值) 進(jìn)行相應(yīng)的包裝處理要拂,因此不管回調(diào)函數(shù)中會(huì)返回一個(gè)什么樣的值抠璃,最終 then 的結(jié)果都是返回一個(gè)新創(chuàng)建的Promise對(duì)象。

使用Promise同時(shí)處理多個(gè)異步請(qǐng)求

在多個(gè)promise對(duì)象都變?yōu)镕ulFilled狀態(tài)的時(shí)候才要進(jìn)行某種處理時(shí):

  • Promise#then

    function getURL(URL) {
      return new Promise((resolve, reject) => {
        const req = new XMLHttpRequest()
        req.open('GET', URL, true)
        req.onload = () => {
          if (req.status === 200) {
            resolve(req.responseText)
          } else {
            reject(new Error(req.statusText))
          }
        }
        req.onerror = () => {
          reject(new Error(req.statusText))
        }
        req.send()
      })
    }
    
    const request = {
      comment: function getComment() {
        return getURL('http://azu.github.io/promises-book/json/comment.json')
                .then(JSON.parse)
      },
      people: function getPeople() {
        return getURL('http://azu.github.io/promises-book/json/people.json')
                .then(JSON.parse)
      }
    }
    
    function main() {
      function recordValue(results, value) {
        results.push(value)
        return results
      }
      // [] 用來保存初始化的值
      const pushValue = recordValue.bind(null, [])
      return request.comment()
                    .then(pushValue)
                    .then(request.people)
                    .then(pushValue)
    }
    // 運(yùn)行的例子
    main()
      .then(value => {
        console.log(value)
      })
      .catch(err => {
        console.error(err)
      })
    

    為了應(yīng)對(duì)這種需要對(duì)多個(gè)異步調(diào)用進(jìn)行統(tǒng)一處理的場(chǎng)景脱惰,Promise準(zhǔn)備了 Promise.all 和 Promise.race 這兩個(gè)靜態(tài)方法搏嗡。

  • Promise.all
    Promise.all 接收一個(gè) Promise對(duì)象的數(shù)組作為參數(shù),當(dāng)這個(gè)數(shù)組里的所有Promise對(duì)象全部變?yōu)閞esolve或reject狀態(tài)的時(shí)候拉一,它才會(huì)去調(diào)用 .then 方法采盒。
    之前例子中的 getURL 返回了一個(gè)promise對(duì)象,它封裝了XHR通信的實(shí)現(xiàn)蔚润。 向 Promise.all 傳遞一個(gè)由封裝了XHR通信的promise對(duì)象數(shù)組的話磅氨,則只有在全部的XHR通信完成之后(變?yōu)镕ulFilled或Rejected狀態(tài))之后,才會(huì)調(diào)用 .then 方法嫡纠。
    上面的例子用Promise.all改寫(除main()以外其他部分不變)如下:

    function main() {
      return Promise.all([request.comment(), request.people()])
    }
    

    與之前的例子相比:

    • main中的處理流程顯得非常清晰
    • Promise.all 接收 promise對(duì)象組成的數(shù)組作為參數(shù)
      在上面的代碼中烦租,request.comment() 和 request.people() 會(huì)同時(shí)開始執(zhí)行,而且每個(gè)promise的結(jié)果(resolve或reject時(shí)傳遞的參數(shù)值)除盏,和傳遞給Promise.all 的promise數(shù)組的順序是一致的叉橱。
      也就是說,這時(shí)候 .then 得到的promise數(shù)組的執(zhí)行結(jié)果的順序是固定的者蠕,即
      [comment, people]窃祝。
main()
  .then(results => {
    console.log(results) // 按照[comment, people]的順序
  })

傳遞給 Promise.all 的promise并不是一個(gè)個(gè)的順序執(zhí)行的,而是同時(shí)開始踱侣、并行執(zhí)行的粪小。

  • Promise.race
    Promise.all 在接收到的所有的對(duì)象promise都變?yōu)?FulFilled 或者 Rejected 狀態(tài)之后才會(huì)繼續(xù)進(jìn)行后面的處理,與之相對(duì)的是 Promise.race 只要有一個(gè)promise對(duì)象進(jìn)入FulFilled 或者 Rejected 狀態(tài)的話抡句,就會(huì)繼續(xù)進(jìn)行后面的處理糕再。

    const winnerPromise = new Promise(resolve => {
      setTimeout(() => {
        console.log('this is winner')
        resolve('this is winner --resolve')
      }, 0)
    })
    const loserPromise = new Promise(resolve => {
      setTimeout(() => {
        console.log('this is loser')
        resolve('this is loser --resolve')
      }, 0)
    })
    
    // 第一個(gè)promise變?yōu)閞esolve(FulFilled)后程序停止
    Promise.race([winnerPromise, loserPromise])
      .then(function (value) {
        console.log(value) // => 'this is winner'
      })
    
    // 輸出結(jié)果
    this is winner
    this is loser
    this is winner --resolve
    

    winnter和loser promise對(duì)象的 setTimeout 方法都會(huì)執(zhí)行完畢,console.log 也會(huì)分別輸出它們的信息玉转。
    也就是說突想,Promise.race 在第一個(gè)promise對(duì)象變?yōu)镕ulfilled之后,并不會(huì)取消其他promise對(duì)象的執(zhí)行究抓。

  • 總結(jié)

    • 使用 promise.then(onFulfilled, onRejected)
      在 onFulfilled 中發(fā)生異常的話猾担,在 onRejected 中是捕獲不到這個(gè)異常的。
    • 在 promise.then(onFulfilled).catch(onRejected) 的情況下
      .then 中產(chǎn)生的異常能在 .catch 中捕獲
    • .then 和 .catch 在本質(zhì)上是沒有區(qū)別的
      需要分場(chǎng)合使用刺下。.then(null, onRejected) 等同于.catch(onRejected)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末绑嘹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子橘茉,更是在濱河造成了極大的恐慌工腋,老刑警劉巖姨丈,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異擅腰,居然都是意外死亡蟋恬,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門趁冈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來歼争,“玉大人,你說我怎么就攤上這事渗勘°迦蓿” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵旺坠,是天一觀的道長(zhǎng)乔遮。 經(jīng)常有香客問我,道長(zhǎng)取刃,這世上最難降的妖魔是什么申眼? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蝉衣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘巷蚪。我一直安慰自己病毡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布屁柏。 她就那樣靜靜地躺著啦膜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪淌喻。 梳的紋絲不亂的頭發(fā)上僧家,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音裸删,去河邊找鬼八拱。 笑死,一個(gè)胖子當(dāng)著我的面吹牛涯塔,可吹牛的內(nèi)容都是我干的肌稻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼匕荸,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼爹谭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起榛搔,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤诺凡,失蹤者是張志新(化名)和其女友劉穎东揣,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腹泌,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嘶卧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了真屯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脸候。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖绑蔫,靈堂內(nèi)的尸體忽然破棺而出运沦,到底是詐尸還是另有隱情,我是刑警寧澤配深,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布携添,位于F島的核電站,受9級(jí)特大地震影響篓叶,放射性物質(zhì)發(fā)生泄漏烈掠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一缸托、第九天 我趴在偏房一處隱蔽的房頂上張望左敌。 院中可真熱鬧,春花似錦俐镐、人聲如沸矫限。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叼风。三九已至,卻和暖如春棍苹,著一層夾襖步出監(jiān)牢的瞬間无宿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工枢里, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留孽鸡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓栏豺,卻偏偏與公主長(zhǎng)得像梭灿,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子冰悠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • Promise 對(duì)象 Promise 的含義 Promise 是異步編程的一種解決方案堡妒,比傳統(tǒng)的解決方案——回調(diào)函...
    neromous閱讀 8,705評(píng)論 1 56
  • 本文適用的讀者 本文寫給有一定Promise使用經(jīng)驗(yàn)的人,如果你還沒有使用過Promise溉卓,這篇文章可能不適合你皮迟,...
    HZ充電大喵閱讀 7,305評(píng)論 6 19
  • 1.promise簡(jiǎn)介 1.1 Promise本意是承諾搬泥,在程序中的意思就是承諾我過一段時(shí)間后會(huì)給你一個(gè)結(jié)果...
    常青_1f93閱讀 836評(píng)論 0 1
  • 前言 本文旨在簡(jiǎn)單講解一下javascript中的Promise對(duì)象的概念,特性與簡(jiǎn)單的使用方法伏尼。并在文末會(huì)附上一...
    _暮雨清秋_閱讀 2,197評(píng)論 0 3
  • 特別說明忿檩,為便于查閱,文章轉(zhuǎn)自https://github.com/getify/You-Dont-Know-JS...
    殺破狼real閱讀 885評(píng)論 0 2