異步解決方案----Promise與Await

前言

異步編程模式在前端開發(fā)過程中,顯得越來越重要促脉。從最開始的XHR到封裝后的Ajax都在試圖解決異步編程過程中的問題辰斋。隨著ES6新標(biāo)準(zhǔn)的到來,處理異步數(shù)據(jù)流又有了新的方案瘸味。我們都知道宫仗,在傳統(tǒng)的ajax請求中,當(dāng)異步請求之間的數(shù)據(jù)存在依賴關(guān)系的時候旁仿,就可能產(chǎn)生很難看的多層回調(diào)藕夫,俗稱'回調(diào)地獄'(callback hell),這卻讓人望而生畏枯冈,Promise的出現(xiàn)讓我們告別回調(diào)函數(shù)毅贮,寫出更優(yōu)雅的異步代碼。在實踐過程中尘奏,卻發(fā)現(xiàn)Promise并不完美滩褥,Async/Await是近年來JavaScript添加的最革命性的的特性之一,Async/Await提供了一種使得異步代碼看起來像同步代碼的替代方法炫加。接下來我們介紹這兩種處理異步編程的方案瑰煎。

一铺然、Promise的原理與基本語法

1.Promise的原理

Promise 是一種對異步操作的封裝,可以通過獨立的接口添加在異步操作執(zhí)行成功酒甸、失敗時執(zhí)行的方法魄健。主流的規(guī)范是 Promises/A+。

Promise中有幾個狀態(tài)

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

  • fulfilled: 成功的操作沽瘦,為表述方便,fulfilled 使用 resolved 代替农尖;

  • rejected: 失敗的操作析恋。


    Promise

pending可以轉(zhuǎn)化為fulfilled或rejected并且只能轉(zhuǎn)化一次,也就是說如果pending轉(zhuǎn)化到fulfilled狀態(tài)盛卡,那么就不能再轉(zhuǎn)化到rejected绿满。并且fulfilled和rejected狀態(tài)只能由pending轉(zhuǎn)化而來,兩者之間不能互相轉(zhuǎn)換窟扑。

2.Promise的基本語法

  • Promise實例必須實現(xiàn)then這個方法

  • then()必須可以接收兩個函數(shù)作為參數(shù)

  • then()返回的必須是一個Promise實例

<script src="https://cdn.bootcss.com/bluebird/3.5.1/bluebird.min.js"></script>//如果低版本瀏覽器不支持Promise,通過cdn這種方式
      <script type="text/javascript">
        function loadImg(src) {
            var promise = new Promise(function (resolve, reject) {
                var img = document.createElement('img')
                img.onload = function () {
                    resolve(img)
                }
                img.onerror = function () {
                    reject('圖片加載失敗')
                }
                img.src = src
            })
            return promise
        }
        var src = 'https://www.imooc.com/static/img/index/logo_new.png'
        var result = loadImg(src)
        result.then(function (img) {
            console.log(1, img.width)
            return img
        }, function () {
            console.log('error 1')
        }).then(function (img) {
            console.log(2, img.height)
        })
     </script>

二漏健、Promise多個串聯(lián)操作

Promise還可以做更多的事情嚎货,比如,有若干個異步任務(wù)蔫浆,需要先做任務(wù)1殖属,如果成功后再做任務(wù)2,任何任務(wù)失敗則不再繼續(xù)并執(zhí)行錯誤處理函數(shù)瓦盛。要串行執(zhí)行這樣的異步任務(wù)洗显,不用Promise需要寫一層一層的嵌套代碼。

有了Promise原环,我們只需要簡單地寫job1.then(job2).then(job3).catch(handleError);
其中job1挠唆、job2和job3都是Promise對象。

比如我們想實現(xiàn)第一個圖片加載完成后嘱吗,再加載第二個圖片玄组,如果其中有一個執(zhí)行失敗,就執(zhí)行錯誤函數(shù):

       var src1 = 'https://www.imooc.com/static/img/index/logo_new.png'
        var result1 = loadImg(src1) //result1是Promise對象
        var src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'
        var result2 = loadImg(src2) //result2是Promise對象
        result1.then(function (img1) {
            console.log('第一個圖片加載完成', img1.width)
            return result2  // 鏈?zhǔn)讲僮?        }).then(function (img2) {
            console.log('第二個圖片加載完成', img2.width)
        }).catch(function (ex) {
            console.log(ex)
        })

這里需注意的是:then 方法可以被同一個 promise 調(diào)用多次谒麦,then 方法必須返回一個 promise 對象俄讹。上例中result1.then如果沒有明文返回Promise實例,就默認(rèn)為本身Promise實例即result1绕德,result1.then返回了result2實例患膛,后面再執(zhí)行.then實際上執(zhí)行的是result2.then

三、Promise常用方法

除了串行執(zhí)行若干異步任務(wù)外耻蛇,Promise還可以并行執(zhí)行異步任務(wù)踪蹬。

試想一個頁面聊天系統(tǒng)胞此,我們需要從兩個不同的URL分別獲得用戶的個人信息和好友列表,這兩個任務(wù)是可以并行執(zhí)行的延曙,用Promise.all()實現(xiàn)如下:

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
// 同時執(zhí)行p1和p2豌鹤,并在它們都完成后執(zhí)行then:
Promise.all([p1, p2]).then(function (results) {
    console.log(results); // 獲得一個Array: ['P1', 'P2']
});

有些時候,多個異步任務(wù)是為了容錯枝缔。比如布疙,同時向兩個URL讀取用戶的個人信息,只需要獲得先返回的結(jié)果即可愿卸。這種情況下灵临,用Promise.race()實現(xiàn):

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
});

由于p1執(zhí)行較快,Promise的then()將獲得結(jié)果'P1'趴荸。p2仍在繼續(xù)執(zhí)行儒溉,但執(zhí)行結(jié)果將被丟棄。

總結(jié):Promise.all接受一個promise對象的數(shù)組发钝,待全部完成之后顿涣,統(tǒng)一執(zhí)行success;

Promise.race接受一個包含多個promise對象的數(shù)組,只要有一個完成酝豪,就執(zhí)行success

接下來我們對上面的例子做下修改涛碑,加深對這兩者的理解:

     var src1 = 'https://www.imooc.com/static/img/index/logo_new.png'
     var result1 = loadImg(src1)
     var src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'
     var result2 = loadImg(src2)
     Promise.all([result1, result2]).then(function (datas) {
         console.log('all', datas[0])//<img src="https://www.imooc.com/static/img/index/logo_new.png">
         console.log('all', datas[1])//<img src="https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg">
     })
     Promise.race([result1, result2]).then(function (data) {
         console.log('race', data)//<img src="https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg">
     })

如果我們組合使用Promise,就可以把很多異步任務(wù)以并行和串行的方式組合起來執(zhí)行

四孵淘、Async/Await簡介與用法

異步操作是 JavaScript 編程的麻煩事蒲障,很多人認(rèn)為async函數(shù)是異步操作的終極解決方案。

1瘫证、Async/Await簡介

  • async/await是寫異步代碼的新方式揉阎,優(yōu)于回調(diào)函數(shù)和Promise。

  • async/await是基于Promise實現(xiàn)的背捌,它不能用于普通的回調(diào)函數(shù)毙籽。

  • async/await與Promise一樣,是非阻塞的毡庆。

  • async/await使得異步代碼看起來像同步代碼惧财,再也沒有回調(diào)函數(shù)。但是改變不了JS單線程扭仁、異步的本質(zhì)垮衷。

2、Async/Await的用法

  • 使用await乖坠,函數(shù)必須用async標(biāo)識

  • await后面跟的是一個Promise實例

  • 需要安裝babel-polyfill搀突,安裝后記得引入 //npm i --save-dev babel-polyfill

   function loadImg(src) {
            const promise = new Promise(function (resolve, reject) {
                const img = document.createElement('img')
                img.onload = function () {
                    resolve(img)
                }
                img.onerror = function () {
                    reject('圖片加載失敗')
                }
                img.src = src
            })
            return promise
        }
     const src1 = 'https://www.imooc.com/static/img/index/logo_new.png'
     const src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'
     const load = async function(){
        const result1 = await loadImg(src1)
        console.log(result1)
        const result2 = await loadImg(src2)
        console.log(result2) 
     }
     load()

當(dāng)函數(shù)執(zhí)行的時候,一旦遇到 await 就會先返回熊泵,等到觸發(fā)的異步操作完成仰迁,再接著執(zhí)行函數(shù)體內(nèi)后面的語句甸昏。

五、Async/Await錯誤處理

await 命令后面的 Promise 對象徐许,運行結(jié)果可能是 rejected施蜜,所以最好把 await 命令放在 try...catch 代碼塊中。try..catch錯誤處理也比較符合我們平常編寫同步代碼時候處理的邏輯雌隅。

async function myFunction() {
  try {
    await somethingThatReturnsAPromise();
  } catch (err) {
    console.log(err);
  }
}

六翻默、為什么Async/Await更好?

Async/Await較Promise有諸多好處恰起,以下介紹其中三種優(yōu)勢:

1. 簡潔

使用Async/Await明顯節(jié)約了不少代碼修械。我們不需要寫.then,不需要寫匿名函數(shù)處理Promise的resolve值检盼,也不需要定義多余的data變量肯污,還避免了嵌套代碼。

2. 中間值

你很可能遇到過這樣的場景吨枉,調(diào)用promise1蹦渣,使用promise1返回的結(jié)果去調(diào)用promise2,然后使用兩者的結(jié)果去調(diào)用promise3貌亭。你的代碼很可能是這樣的:

const makeRequest = () => {
  return promise1()
    .then(value1 => {
      return promise2(value1)
        .then(value2 => {        
          return promise3(value1, value2)
        })
    })
}

使用async/await的話剂桥,代碼會變得異常簡單和直觀

const makeRequest = async () => {
  const value1 = await promise1()
  const value2 = await promise2(value1)
  return promise3(value1, value2)
}

3.條件語句

下面示例中,需要獲取數(shù)據(jù)属提,然后根據(jù)返回數(shù)據(jù)決定是直接返回,還是繼續(xù)獲取更多的數(shù)據(jù)美尸。

const makeRequest = () => {
  return getJSON()
    .then(data => {
      if (data.needsAnotherRequest) {
        return makeAnotherRequest(data)
          .then(moreData => {
            console.log(moreData)
            return moreData
          })
      } else {
        console.log(data)
        return data
      }
    })
}

代碼嵌套(6層)可讀性較差冤议,它們傳達(dá)的意思只是需要將最終結(jié)果傳遞到最外層的Promise。使用async/await編寫可以大大地提高可讀性:

const makeRequest = async () => {
  const data = await getJSON()
  if (data.needsAnotherRequest) {
    const moreData = await makeAnotherRequest(data);
    console.log(moreData)
    return moreData
  } else {
    console.log(data)
    return data    
  }
}

如果覺得文章對你有些許幫助师坎,歡迎在我的GitHub博客點贊和關(guān)注恕酸,感激不盡!

參考文章

Async/Await替代Promise的6個理由

前端的異步解決方案之Promise和Await/Async

廖雪峰的Javascript教程

[譯] Promises/A+ 規(guī)范

async 函數(shù)的含義和用法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胯陋,一起剝皮案震驚了整個濱河市蕊温,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌遏乔,老刑警劉巖义矛,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異盟萨,居然都是意外死亡凉翻,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門捻激,熙熙樓的掌柜王于貴愁眉苦臉地迎上來制轰,“玉大人前计,你說我怎么就攤上這事±龋” “怎么了男杈?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長调俘。 經(jīng)常有香客問我伶棒,道長,這世上最難降的妖魔是什么脉漏? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任苞冯,我火速辦了婚禮,結(jié)果婚禮上侧巨,老公的妹妹穿的比我還像新娘舅锄。我一直安慰自己,他們只是感情好司忱,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布皇忿。 她就那樣靜靜地躺著,像睡著了一般坦仍。 火紅的嫁衣襯著肌膚如雪鳍烁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天繁扎,我揣著相機與錄音幔荒,去河邊找鬼。 笑死梳玫,一個胖子當(dāng)著我的面吹牛爹梁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播提澎,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼姚垃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了盼忌?” 一聲冷哼從身側(cè)響起积糯,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谦纱,沒想到半個月后看成,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡跨嘉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年绍昂,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡窘游,死狀恐怖唠椭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情忍饰,我是刑警寧澤贪嫂,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站艾蓝,受9級特大地震影響力崇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赢织,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一亮靴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧于置,春花似錦茧吊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至话速,卻和暖如春讶踪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背泊交。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工乳讥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人廓俭。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓云石,卻偏偏與公主長得像,于是被迫代替她去往敵國和親白指。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

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

  • 異步編程對JavaScript語言太重要酵紫。Javascript語言的執(zhí)行環(huán)境是“單線程”的告嘲,如果沒有異步編程,根本...
    呼呼哥閱讀 7,301評論 5 22
  • 簡單介紹下這幾個的關(guān)系為方便起見 用以下代碼為例簡單介紹下這幾個東西的關(guān)系奖地, async 在函數(shù)聲明前使用asyn...
    _我和你一樣閱讀 21,216評論 1 24
  • 弄懂js異步 講異步之前橄唬,我們必須掌握一個基礎(chǔ)知識-event-loop。 我們知道JavaScript的一大特點...
    DCbryant閱讀 2,706評論 0 5
  • Promise對象是一種解決異步問題的方法参歹,還有的解決方案是asyns 和 await (es7) 這么是目前的終...
    站在大神的肩膀上看世界閱讀 1,259評論 0 6
  • 透徹掌握Promise的使用仰楚,讀這篇就夠了 Promise的重要性我認(rèn)為我沒有必要多講,概括起來說就是必須得掌握,...
    穿牛仔褲的蚊子閱讀 2,120評論 0 16