基于Promise和Async/Await的異步流程控制

簡介

很久以前绍赛,在Promise出現(xiàn)之前,編寫js的異步代碼是一件十分蛋疼的事情辑畦,一旦異步流程一復(fù)雜吗蚌,回調(diào)地獄就會(huì)等著你,所以基于node.js的服務(wù)端編碼開發(fā)的體驗(yàn)非常差纯出,項(xiàng)目的可維護(hù)性和可讀性也非常差蚯妇,這也是最初node.js最為人所詬病的地方。

在現(xiàn)代的js項(xiàng)目中暂筝,回調(diào)函數(shù)已經(jīng)基本被Promise取代箩言,而async/await的出現(xiàn)更是解決的promise在使用中會(huì)產(chǎn)生大量的膠水代碼,以及不同Promise之間共享數(shù)據(jù)困難的問題焕襟,這又大大提升的編寫異步流程的開發(fā)體驗(yàn)陨收,但是async/await本身是基于promise的,而在很多情況下鸵赖,async/await并不能完全取代promise的位置务漩,理解promise是編寫高質(zhì)量的異步代碼的基礎(chǔ)。下面我們就來探討一下在js中的異步流程控制的問題卫漫。如何在異步代碼中保證異步流程的執(zhí)行效率以及可讀性和可維護(hù)性菲饼。以下為一個(gè)例子:

如何制作一個(gè)披薩?

  1. 制作餅皮(dough)

  2. 制作醬汁(sauce)

  3. 品嘗一下醬汁列赎,根據(jù)醬汁的口味決定放哪種芝士(cheese)

第一版實(shí)現(xiàn)

async function makePizza(sauceType = 'red') {
  
  let dough  = await makeDough();
  let sauce  = await makeSauce(sauceType);
  let cheese = await grateCheese(sauce.determineCheese());
  
  dough.add(sauce);
  dough.add(cheese);
  
  return dough;

}

基于async/await宏悦,看起來邏輯很清晰镐确,但是它最大的問題是這不是一個(gè)最優(yōu)的異步流程控制。

第二版實(shí)現(xiàn)

// 原來的流程
|-------- dough --------> |-------- sauce --------> |-- cheese -->

// 改進(jìn)的流程
|-------- dough -------->
|-------- sauce --------> |-- cheese -->

async function makePizza(sauceType = 'red') {
  
  let [ dough, sauce ] =
    await Promise.all([ makeDough(), makeSauce(sauceType) ]);
  let cheese = await grateCheese(sauce.determineCheese());
  
  dough.add(sauce);
  dough.add(cheese);
  
  return dough;
}

效率比第一版就高了很多饼煞。使用promise.all并行執(zhí)行異步任務(wù)源葫,流程也比較清晰。

第三版實(shí)現(xiàn)

由于制作dough的時(shí)間是比較長的砖瞧,我們的流程其實(shí)還有改進(jìn)的空間息堂,如下。

// 上一步改進(jìn)的流程
|-------- dough -------->
|--- sauce --->           |-- cheese -->

// 進(jìn)一步改進(jìn)的流程
|--------- dough --------->
|---- sauce ----> |-- cheese -->

function makePizza(sauceType = 'red') {
  let doughPromise  = makeDough();
  let saucePromise  = makeSauce(sauceType);
  let cheesePromise = saucePromise.then(sauce => {
    return grateCheese(sauce.determineCheese());
  });
  
  return Promise.all([ doughPromise, saucePromise, cheesePromise ])
    .then(([ dough, sauce, cheese ]) => {
      
      dough.add(sauce);
      dough.add(cheese);
      
      return dough;
    });
}

現(xiàn)在的異步流程應(yīng)該是最優(yōu)的了块促,但是問題來了荣堰,這樣的流程用async/await表示的話就有點(diǎn)麻煩了,我們這里先用promise實(shí)現(xiàn)了這個(gè)最優(yōu)流程竭翠,可以看到promise的弊端很明顯振坚,充滿了膠水代碼,我們?cè)僖膊荒芟裎覀兊牡谝话鎸?shí)現(xiàn)一樣斋扰,一眼就能看出我們這個(gè)函數(shù)的異步流程是怎么樣的了渡八,可讀性下降了。

第四版實(shí)現(xiàn)

// 最優(yōu)流程
|--------- dough --------->
|---- sauce ----> |-- cheese -->

async function makePizza(sauceType = 'red') {
  
  let doughPromise = makeDough();
  let saucePromise = makeSauce(sauceType);
  
  let sauce  = await saucePromise;
  let cheese = await grateCheese(sauce.determineCheese());
  let dough  = await doughPromise;
  
  dough.add(sauce);
  dough.add(cheese);
  
  return dough;
}

這一版實(shí)現(xiàn)传货,代碼看起來整潔了很多屎鳍,但是它的異步流程更加不清晰了,通過提前創(chuàng)建promise然后在之后再進(jìn)行await问裕,我們得到了整潔的代碼逮壁,但是卻使得流程更加不清晰,隱式的執(zhí)行promise然后await僻澎,讓await的語義變得很不明了貌踏。

第五版實(shí)現(xiàn)

async function makePizza(sauceType = 'red') {
  
  let prepareDough  = memoize(async () => makeDough());
  let prepareSauce  = memoize(async () => makeSauce(sauceType));
  let prepareCheese = memoize(async () => {
    return grateCheese((await prepareSauce()).determineCheese());
  });
  
  let [ dough, sauce, cheese ] = 
    await Promise.all([
      prepareDough(), prepareSauce(), prepareCheese()
    ]);
    
  dough.add(sauce);
  dough.add(cheese);
  
  return dough;
}

這個(gè)版本相比于上一個(gè)版本的區(qū)別主要是現(xiàn)在我們不再提前創(chuàng)建promise十饥,而保存為一個(gè)生成函數(shù)窟勃,并且使用memoize緩存結(jié)果,從而使得promise只需要resolve一次就可以緩存結(jié)果逗堵。這樣子的異步流程相比于上一個(gè)版本就清楚了很多秉氧,而代碼依然可以保持很好的可讀性。

總結(jié)

感覺Promise.all和await/async的結(jié)合依然不是很優(yōu)雅蜒秤,總感覺有點(diǎn)難受汁咏,在未來,js的異步流程控制肯定還會(huì)有更優(yōu)雅的解決方案作媚。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末攘滩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子纸泡,更是在濱河造成了極大的恐慌漂问,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蚤假,居然都是意外死亡栏饮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門磷仰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來袍嬉,“玉大人,你說我怎么就攤上這事灶平∷磐ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵逢享,是天一觀的道長泵殴。 經(jīng)常有香客問我,道長拼苍,這世上最難降的妖魔是什么笑诅? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮疮鲫,結(jié)果婚禮上吆你,老公的妹妹穿的比我還像新娘。我一直安慰自己俊犯,他們只是感情好妇多,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著燕侠,像睡著了一般者祖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绢彤,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天七问,我揣著相機(jī)與錄音,去河邊找鬼茫舶。 笑死械巡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的饶氏。 我是一名探鬼主播讥耗,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼疹启!你這毒婦竟也來了古程?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤喊崖,失蹤者是張志新(化名)和其女友劉穎挣磨,沒想到半個(gè)月后菲宴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡趋急,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年喝峦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呜达。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谣蠢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出查近,到底是詐尸還是另有隱情眉踱,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布霜威,位于F島的核電站谈喳,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏戈泼。R本人自食惡果不足惜婿禽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望大猛。 院中可真熱鬧扭倾,春花似錦、人聲如沸挽绩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唉堪。三九已至模聋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間唠亚,已是汗流浹背链方。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留趾撵,地道東北人侄柔。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像占调,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子移剪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

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

  • 異步編程對(duì)JavaScript語言太重要究珊。Javascript語言的執(zhí)行環(huán)境是“單線程”的,如果沒有異步編程纵苛,根本...
    呼呼哥閱讀 7,311評(píng)論 5 22
  • 一.非阻塞和異步 借用知乎用戶嚴(yán)肅的回答在此總結(jié)下剿涮,同步和異步是針對(duì)消息通信機(jī)制言津,同步代表一個(gè)client發(fā)出一個(gè)...
    Daniel_adu閱讀 1,826評(píng)論 0 8
  • javascript的運(yùn)行機(jī)制是單線程處理瞬浓,即只有上一個(gè)任務(wù)完成后初婆,才會(huì)執(zhí)行下一個(gè)任務(wù),這種機(jī)制也被稱為“同步”猿棉。...
    我是xy閱讀 3,893評(píng)論 1 6
  • 單線程與異步 Javascript是單線程運(yùn)行磅叛、支持異步機(jī)制的語言。進(jìn)入正題之前萨赁,我們有必要先理解這種運(yùn)行方式弊琴。 ...
    貝聊科技閱讀 625評(píng)論 0 0
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券杖爽,享受所有官網(wǎng)優(yōu)惠敲董,并抽取幸運(yùn)大...
    HetfieldJoe閱讀 11,026評(píng)論 26 95