async 和 await

這段時(shí)間一直在重構(gòu)項(xiàng)目曲初,遇見很多請(qǐng)求“高并發(fā)”钮热,因?yàn)樯婕暗蕉鄠€(gè)請(qǐng)求又或者多個(gè)連續(xù)請(qǐng)求校辩。之所以給高并發(fā)帶上引號(hào)窘问,因?yàn)槊鎸?duì)大量請(qǐng)求的時(shí)候,我們需要調(diào)整好姿勢(shì)宜咒,怎樣去好好的去寫異步回調(diào)惠赫,弄清各個(gè)請(qǐng)求的順序,稍微不注意可能就掉坑了故黑,可能調(diào)試半天看著vue-devtool控制臺(tái)打印的自己以為的“完美的數(shù)據(jù)”儿咱,但是頁面各種顯示不聽話。哦场晶,那說明你掉坑了混埠!
理清好執(zhí)行的先后順序,其次再寫回調(diào)的時(shí)候我們就要選好正確的方法和正確的姿勢(shì)峰搪,這樣才不會(huì)造成將來你寫的代碼你認(rèn)不清的尷尬岔冀,同時(shí)感覺代碼一目了然凯旭!
同時(shí)考慮到異步回調(diào)我們需要理解一些知識(shí):
js的運(yùn)行機(jī)制:
在代碼運(yùn)行時(shí)會(huì)形成任務(wù)隊(duì)列概耻,分為同步任務(wù)隊(duì)列和異步任務(wù)對(duì)列,同步隊(duì)列優(yōu)先加載罐呼,異步任務(wù)隊(duì)列會(huì)形成隊(duì)列任務(wù)池鞠柄,定時(shí)器不會(huì)一下加載到異步任務(wù),而是在設(shè)定的時(shí)間后加載到異步任務(wù)嫉柴,即使設(shè)置為0厌杜,瀏覽器也有它的響應(yīng)時(shí)間,以前是10ms.現(xiàn)在是4ms.異步任務(wù)包括dom 事件计螺,定時(shí)器夯尽,promise
首先作為異步回調(diào)的功能和作用不去作過多解釋,對(duì)于js這種單線程異步回調(diào)是性能優(yōu)化的一些點(diǎn)登馒。
異步回調(diào)我覺得主要有兩方面作用:

  • 不阻礙程序運(yùn)行匙握,將一些延時(shí)較久的函數(shù)異步執(zhí)行,不妨礙正常同步運(yùn)行的代碼陈轿;
  • 一個(gè)函數(shù)必須在某個(gè)函數(shù)執(zhí)行完成后才能運(yùn)行圈纺,比如說需要用函數(shù)執(zhí)行完后的某些數(shù)據(jù)秦忿;
    首先寫異步回調(diào)的姿勢(shì)大概有這樣幾種
  1. 直接函數(shù)套函數(shù)(通俗的講)
    就像這樣
function fn(callback) {
  setTimeout(() => {
    callback()
  }, 1000);
}
function f1() {
  console.log('f1')
}
fn(f1)
// fn
// f1

這樣我們感覺還行,還不錯(cuò)蛾娶,還能接受灯谣,那如果有另外一個(gè)f2函數(shù),f1像fn那樣蛔琅,在多個(gè)f3:

function fn(callback) {
  setTimeout(() => {
    callback(f2)
  }, 1000);
  console.log('fn')
}
function f1(callback) {
  setTimeout(() => {
    callback(f3)
  }, 1000);
  console.log('f1')
}
function f2(callback) {
  setTimeout(() => {
    callback(f3)
  }, 1000);
  console.log('f2')
}
function f3() {
  console.log('f3');
}
fn(f1)
// fn
// f1
// f2
//f3

這樣看著代碼也沒這么亂胎许,但是感覺把自己調(diào)懵了,如果想看出它的一些過程揍愁,我們將函數(shù)改寫一下

function fn(callback) {
  setTimeout(() => {
    callback((callback) => {
      setTimeout(() => {
        callback()
      }, 1000);
      console.log('f2')
    })
  }, 1000);
  console.log('fn')
}
fn((callback) => {
  setTimeout(() => {
    callback((callback) => {
      console.log('f3')
    })
  }, 1000);
  console.log('f1')
})
// fn
// f1
// f2
// f3

如果連起來看呐萨,分清之前的f1,f2,f3函數(shù)很困難吧,這還只是三個(gè)回調(diào)函數(shù)莽囤,有時(shí)候不僅僅這些吧谬擦,如果再來兩個(gè),咋樣朽缎?惨远??
所以傳統(tǒng)方法晦澀難懂话肖。北秽。。最筒。

  1. 第二種方法看起來可能就比較爽了------Promise
    Promise 是異步編程的一種解決方案贺氓,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件,更合理和更強(qiáng)大床蜘。它由社區(qū)最早提出和實(shí)現(xiàn)辙培,ES6 將其寫進(jìn)了語言標(biāo)準(zhǔn),統(tǒng)一了用法邢锯,原生提供了Promise對(duì)象扬蕊。
    所謂Promise,簡(jiǎn)單說就是一個(gè)容器丹擎,里面保存著某個(gè)未來才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果尾抑。從語法上說,Promise 是一個(gè)對(duì)象蒂培,從它可以獲取異步操作的消息再愈。Promise 提供統(tǒng)一的 API,各種異步操作都可以用同樣的方法進(jìn)行處理
    前兩句話是摘自阮一峰老師的《es6入門》
    我理解的Promise其實(shí)就是給你封裝好的一個(gè)異步對(duì)象护戳,本身有resolve和reject參數(shù)翎冲,當(dāng)然也是函數(shù),其實(shí)這里叫函數(shù)也不太好灸异,我們不如說叫“自定義鉤子”府适,當(dāng)然這個(gè)還不像我們常說的鉤子函數(shù)那樣羔飞。Promise對(duì)象外部暴露的了兩個(gè)分別對(duì)應(yīng)的是成功之后的then函數(shù),另一個(gè)是reject函數(shù)檐春。說白了這兩個(gè)函數(shù)是由用來設(shè)計(jì)resolve和reject函數(shù)的逻淌。Promise只負(fù)責(zé)將你寫的函數(shù)在內(nèi)部調(diào)用,同時(shí)將他的返回值有兩個(gè)(這里說的兩個(gè)是兩種結(jié)果)傳遞出來疟暖,成功之后自然就是resolve(data),失敗是reject(data)卡儒,當(dāng)然我們寫resolve的代碼塊其實(shí)就是Promise實(shí)例執(zhí)行完后對(duì)應(yīng)的then函數(shù)的執(zhí)行,當(dāng)然data就是then函數(shù)回調(diào)的參數(shù)俐巴,當(dāng)然reject和catch()也是一樣的道理骨望。其實(shí)看著這么像函數(shù)傳參的過程,也這么像“依賴注入”的趕腳欣舵。
    不說別的了擎鸠,直接將上面函數(shù)改寫一下:
function fn() {
  console.log('fn');
  return new Promise(resolve => {
    setTimeout(() => {
      resolve()
    }, 1000);
  })
}
function f1() {
  console.log('f1')
  return new Promise(resolve => {
    setTimeout(() => {
      resolve()
    }, 1000);
  })
}
function f2() {
  console.log('f2')
  return new Promise(resolve2 => {
    setTimeout(() => {
      resolve2()
    }, 1000);
  })
}
function f3() {
  console.log('f3');
}
fn().then(() => {
  f1().then(() => {
    f2().then(() => {
      f3()
    })
  })
})
// fn
// f1
// f2
// f3

等到講aync和await時(shí)再好好講講Promise,今天的項(xiàng)目有一個(gè)特點(diǎn)就被埋坑了
有了Promise對(duì)象缘圈,就可以將異步操作以同步操作的流程表達(dá)出來劣光,避免了層層嵌套的回調(diào)函數(shù)。此外糟把,Promise對(duì)象提供統(tǒng)一的接口绢涡,使得控制異步操作更加容易,看著上面的代碼是不是清爽多了,但是感覺函數(shù)一多結(jié)構(gòu)看著似乎也不那么友好遣疯,同時(shí)Promise也有弊端就是獲取錯(cuò)誤信息的時(shí)候雄可,這些就不贅述了,網(wǎng)上應(yīng)該有很多介紹的缠犀,今天的主角是async 和 await

  1. async 和 await
    這兩個(gè)方法其實(shí)就是Generator 函數(shù)的語法糖数苫。詳情請(qǐng)見阮一峰的es6
    先說說背景,今天做一個(gè)黑名單的需求:
    拉取軟件列表的時(shí)候夭坪,首先我需要對(duì)list中存在的黑名單來個(gè)請(qǐng)求黑名單的請(qǐng)求文判,同時(shí)請(qǐng)求完成后我們需要將黑名單list存儲(chǔ)在vue的data對(duì)象里过椎,然后再通過對(duì)list進(jìn)行過濾室梅,對(duì)是黑名單的item進(jìn)行軟件圖標(biāo)的置灰操作,同時(shí)在不同的分類我們還需要進(jìn)行判斷是否進(jìn)行過黑名單的操作疚宇,若果進(jìn)行過就可以對(duì)本分組進(jìn)行鎖定和解鎖操作亡鼠,同時(shí)后續(xù)操作我們還需要拉取一個(gè)已經(jīng)鎖定分組的集合,同時(shí)是判定當(dāng)前分組是否屬于已經(jīng)鎖定分組敷待,相應(yīng)的在頁面中對(duì)當(dāng)前分組是顯示解鎖按鈕還是鎖定按鈕
    鑒于涉及到這么多的請(qǐng)求间涵,一個(gè)請(qǐng)求完成后需要完成幾個(gè)請(qǐng)求才能進(jìn)行相應(yīng)參數(shù),同時(shí)需要請(qǐng)求的先后順序很明確榜揖。
    剛開始考慮就用Promise勾哩,因?yàn)榉庋b的ajax方法就是基于axios的抗蠢,但涉及到這么多連續(xù)請(qǐng)求先別說結(jié)構(gòu)不友好,可能出錯(cuò)你都不知道怎么出的思劳,所以我打算用async和await:
    當(dāng)然咱們還是先把上面的那個(gè)例子改一下再說項(xiàng)目中的問題:
function f1() {
  console.log('f1')
  return new Promise(resolve => {
    setTimeout(() => {
      resolve()
    }, 1000);
  })
}
function f2() {
  console.log('f2')
  return new Promise(resolve2 => {
    setTimeout(() => {
      resolve2()
    }, 1000);
  })
}
function f3() {
  console.log('f3');
}
async function fn() {
  console.log('fn');
  await f1()
  await f2()
  f3()
}
fn()
// fn
// f1
// f2
// f3

這樣寫代碼是不是看起來清爽多了迅矛!
今天寫邏輯的時(shí)候犯了一個(gè)影響智商的錯(cuò)看代碼和執(zhí)行效果
在公司寫的代碼不變貼上來,所以就來模擬一下函數(shù)的執(zhí)行

  async function fn() {
    await f1()
    console.log('fn')
  }
  function f1() {
    new Promise(resolve => {
      setTimeout(() => {
        console.log('f1')
        resolve()
      }, 1000);
    })
  }
  fn()
// fn
// f1
async function fn() {
    await f1()
    console.log('fn')
  }
  function f1() {
   return new Promise(resolve => {
      setTimeout(() => {
        console.log('f1')
        resolve()
      }, 1000);
    })
  }
  fn()
// f1
// fn

看看兩次執(zhí)行的結(jié)果
第一個(gè)函數(shù)塊寫的時(shí)候完全不按異步執(zhí)行去操作潜叛,總是像一般函數(shù)那樣秽褒,await當(dāng)成了異步隊(duì)列,我只想說明注意的一個(gè)點(diǎn)事用Promise時(shí)的return威兜。
我們應(yīng)該了解下面這些事:
Promise有一個(gè)性質(zhì)就是立即執(zhí)行销斟,我們需要將它外包一層函數(shù)(就叫fn),當(dāng)我們await fn(),請(qǐng)記住fn只是為了不讓promise立即執(zhí)行,所以我們一定得在fn函數(shù)中返回promise椒舵,如果不返回蚂踊,應(yīng)該知道沒有返回值的函數(shù)的執(zhí)行結(jié)果是undefined,還執(zhí)行個(gè)毛線呀笔宿!
記住函數(shù)沒有返回值執(zhí)行結(jié)果就是undefinedc彩啤!4敕ァ特纤!
同時(shí)還有一個(gè)比較常見的問題:

async getList() {
        fetchList(this.listQuery).then(
          ({data}) => {
          let list = data.list
          this.softTotalNum = data.total
          // 獲取黑名單list
          await this.getBlackList()
          list.map(item => {
            if (this.soft_ids.indexOf(item.soft_id) >= 0) {
              item['is_hidden'] = 1
            }
            return item
          })
          this.list = list;
        })
      },
這時(shí)候報(bào)的錯(cuò)

其實(shí)應(yīng)該這樣寫

getList() {
        fetchList(this.listQuery).then(
         async ({data}) => {
          let list = data.list
          this.softTotalNum = data.total
          // 獲取黑名單list
          await this.getBlackList()
          list.map(item => {
            if (this.soft_ids.indexOf(item.soft_id) >= 0) {
              item['is_hidden'] = 1
            }
            return item
          })
          this.list = list;
        })
      },

一定要在你用的await的最近的父級(jí)用async聲明異步函數(shù),其他的頂級(jí)父級(jí)沒有必要寫async

今天遇到坑后總結(jié)了下面三點(diǎn):

  1. async是一個(gè)異步函數(shù)聲明詞侥加,await必須在async函數(shù)中使用捧存,await后面應(yīng)該你用一個(gè)延時(shí)函數(shù),當(dāng)然你用一個(gè)一般函數(shù)也行担败,就是立即執(zhí)行而已昔穴。根據(jù)單詞的字面意思,await我們可以理解為必須等我后面的函數(shù)執(zhí)行完之后提前,下面的代碼才能運(yùn)行吗货。
  2. await的最近父級(jí)必須是async函數(shù),否則會(huì)報(bào)“await is a reserved word”錯(cuò)狈网。
  3. await對(duì)應(yīng)的如果類似于promise的函數(shù)宙搬,鑒于promise的立即執(zhí)行的特點(diǎn),我們需要將它外包一層函數(shù)(就叫fn),當(dāng)我們await fn(),請(qǐng)記住fn只是問了不讓promise立即執(zhí)行拓哺,所以我們一定得在fn函數(shù)中返回promise勇垛,如果不返回,應(yīng)該知道沒有返回值的函數(shù)的執(zhí)行結(jié)果是undefined士鸥,還執(zhí)行個(gè)毛線呀闲孤!
    以上就是一些按async和await的一些知識(shí),當(dāng)然這是es7的東西烤礁,記得babel編譯K匣7收铡!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末勤众,一起剝皮案震驚了整個(gè)濱河市建峭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌决摧,老刑警劉巖亿蒸,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異掌桩,居然都是意外死亡边锁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門波岛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茅坛,“玉大人,你說我怎么就攤上這事则拷」北停” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵煌茬,是天一觀的道長斥铺。 經(jīng)常有香客問我,道長坛善,這世上最難降的妖魔是什么晾蜘? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮眠屎,結(jié)果婚禮上剔交,老公的妹妹穿的比我還像新娘。我一直安慰自己改衩,他們只是感情好岖常,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著葫督,像睡著了一般竭鞍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上候衍,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天笼蛛,我揣著相機(jī)與錄音洒放,去河邊找鬼蛉鹿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛往湿,可吹牛的內(nèi)容都是我干的妖异。 我是一名探鬼主播惋戏,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼他膳!你這毒婦竟也來了响逢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤棕孙,失蹤者是張志新(化名)和其女友劉穎舔亭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蟀俊,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钦铺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肢预。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矛洞。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖烫映,靈堂內(nèi)的尸體忽然破棺而出沼本,到底是詐尸還是另有隱情,我是刑警寧澤锭沟,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布抽兆,位于F島的核電站,受9級(jí)特大地震影響族淮,放射性物質(zhì)發(fā)生泄漏郊丛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一瞧筛、第九天 我趴在偏房一處隱蔽的房頂上張望厉熟。 院中可真熱鬧,春花似錦较幌、人聲如沸揍瑟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绢片。三九已至,卻和暖如春岛琼,著一層夾襖步出監(jiān)牢的瞬間底循,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國打工槐瑞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留熙涤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像祠挫,于是被迫代替她去往敵國和親那槽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359