從promise到async鞭光,異步編程

Javascript語(yǔ)言的執(zhí)行環(huán)境是"單線程"(single thread),所謂"單線程"泞遗,就是指一次只能完成一件任務(wù)惰许。如果有多個(gè)任務(wù),就必須排隊(duì)刹孔,前面一個(gè)任務(wù)完成啡省,再執(zhí)行后面一個(gè)任務(wù),以此類推髓霞。這種模式的好處是實(shí)現(xiàn)起來比較簡(jiǎn)單卦睹,執(zhí)行環(huán)境相對(duì)單純;壞處是只要有一個(gè)任務(wù)耗時(shí)很長(zhǎng)方库,后面的任務(wù)都必須排隊(duì)等著结序,會(huì)拖延整個(gè)程序的執(zhí)行。常見的瀏覽器無(wú)響應(yīng)(假死)纵潦,往往就是因?yàn)槟骋欢蜫avascript代碼長(zhǎng)時(shí)間運(yùn)行(比如死循環(huán))徐鹤,導(dǎo)致整個(gè)頁(yè)面卡在這個(gè)地方,其他任務(wù)無(wú)法執(zhí)行邀层。

為了解決這個(gè)問題返敬,Javascript語(yǔ)言將任務(wù)的執(zhí)行模式分成兩種:同步(Synchronous)和異步(Asynchronous)。"同步模式"就是上一段的模式寥院,后一個(gè)任務(wù)等待前一個(gè)任務(wù)結(jié)束劲赠,然后再執(zhí)行,程序的執(zhí)行順序與任務(wù)的排列順序是一致的秸谢、同步的悯恍;"異步模式"則完全不同商叹,每一個(gè)任務(wù)有一個(gè)或多個(gè)回調(diào)函數(shù)(callback)叉袍,前一個(gè)任務(wù)結(jié)束后荠列,不是執(zhí)行后一個(gè)任務(wù),而是執(zhí)行回調(diào)函數(shù)臭蚁,后一個(gè)任務(wù)則是不等前一個(gè)任務(wù)結(jié)束就執(zhí)行最铁,所以程序的執(zhí)行順序與任務(wù)的排列順序是不一致的讯赏、異步的。

"異步模式"非常重要炭晒。在瀏覽器端待逞,耗時(shí)很長(zhǎng)的操作都應(yīng)該異步執(zhí)行,避免瀏覽器失去響應(yīng)网严,最好的例子就是Ajax操作。在服務(wù)器端嗤无,"異步模式"甚至是唯一的模式震束,因?yàn)閳?zhí)行環(huán)境是單線程的,如果允許同步執(zhí)行所有http請(qǐng)求当犯,服務(wù)器性能會(huì)急劇下降垢村,很快就會(huì)失去響應(yīng)。

在ES6誕生以前嚎卫,異步編程的方式大概有下面四種:回調(diào)函數(shù)嘉栓、事件監(jiān)聽、發(fā)布/訂閱拓诸、Promise對(duì)象侵佃。ES6中,引入了Generator函數(shù)奠支;ES7中馋辈,async更是將異步編程帶入了一個(gè)全新的階段。

Promise對(duì)象

ES6 規(guī)定倍谜,Promise對(duì)象是一個(gè)構(gòu)造函數(shù)迈螟,用來生成Promise實(shí)例。Promise構(gòu)造函數(shù)接受一個(gè)函數(shù)作為參數(shù)尔崔,該函數(shù)的兩個(gè)參數(shù)分別是resolve和reject答毫。它們是兩個(gè)函數(shù),由 JavaScript 引擎提供季春,不用自己部署洗搂。

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 異步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

resolve函數(shù)的作用是,將Promise對(duì)象的狀態(tài)從 pending 變?yōu)?resolved鹤盒,在異步操作成功時(shí)調(diào)用蚕脏,并將異步操作的結(jié)果,作為參數(shù)傳遞出去侦锯;reject函數(shù)的作用是驼鞭,將Promise對(duì)象的狀態(tài)從pending 變?yōu)?rejected,在異步操作失敗時(shí)調(diào)用尺碰,并將異步操作報(bào)出的錯(cuò)誤挣棕,作為參數(shù)傳遞出去译隘。

Promise實(shí)例生成以后,可以用then方法分別指定resolved狀態(tài)和rejected狀態(tài)的回調(diào)函數(shù)洛心。then方法可以接受兩個(gè)回調(diào)函數(shù)作為參數(shù)固耘。第一個(gè)回調(diào)函數(shù)是Promise對(duì)象的狀態(tài)變?yōu)閞esolved時(shí)調(diào)用,第二個(gè)回調(diào)函數(shù)是Promise對(duì)象的狀態(tài)變?yōu)閞ejected時(shí)調(diào)用词身。其中厅目,第二個(gè)函數(shù)是可選的,不一定要提供法严。這兩個(gè)函數(shù)都接受Promise對(duì)象傳出的值作為參數(shù)损敷。

Generator函數(shù)

Generator 函數(shù)是協(xié)程在 ES6 的實(shí)現(xiàn),最大特點(diǎn)就是可以交出函數(shù)的執(zhí)行權(quán)(即暫停執(zhí)行)深啤。

整個(gè) Generator 函數(shù)就是一個(gè)封裝的異步任務(wù)拗馒,或者說是異步任務(wù)的容器。異步操作需要暫停的地方溯街,都用yield語(yǔ)句注明诱桂。Generator 函數(shù)的執(zhí)行方法如下。

function* gen(x) {
  var y = yield x + 2;
  return y;
}

var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }

上面代碼中呈昔,調(diào)用 Generator 函數(shù)挥等,會(huì)返回一個(gè)內(nèi)部指針(即遍歷器)g。這是 Generator 函數(shù)不同于普通函數(shù)的另一個(gè)地方韩肝,即執(zhí)行它不會(huì)返回結(jié)果触菜,返回的是指針對(duì)象。調(diào)用指針g的next方法哀峻,會(huì)移動(dòng)內(nèi)部指針(即執(zhí)行異步任務(wù)的第一段)涡相,指向第一個(gè)遇到的yield語(yǔ)句,上例是執(zhí)行到x + 2為止剩蟀。

換言之催蝗,next方法的作用是分階段執(zhí)行Generator函數(shù)。每次調(diào)用next方法育特,會(huì)返回一個(gè)對(duì)象丙号,表示當(dāng)前階段的信息(value屬性和done屬性)。value屬性是yield語(yǔ)句后面表達(dá)式的值缰冤,表示當(dāng)前階段的值犬缨;done屬性是一個(gè)布爾值,表示 Generator 函數(shù)是否執(zhí)行完畢棉浸,即是否還有下一個(gè)階段怀薛。

由于Generator函數(shù)無(wú)法自動(dòng)執(zhí)行,所以需要用到Thunk 函數(shù)或co模塊來實(shí)現(xiàn)自動(dòng)化執(zhí)行迷郑。

Async函數(shù)

async 函數(shù)就是 Generator 函數(shù)的語(yǔ)法糖枝恋。將 Generator 函數(shù)的星號(hào)(*)替換成async创倔,將yield替換成await,僅此而已焚碌。
async 函數(shù)的實(shí)現(xiàn)原理畦攘,就是將 Generator 函數(shù)和自動(dòng)執(zhí)行器,包裝在一個(gè)函數(shù)里十电。

  async function fn(args) {
  // ...
}

// 等同于

function fn(args) {
  return spawn(function* () {    //spawn函數(shù)就是自動(dòng)執(zhí)行器
    // ...
  });
}

async函數(shù)對(duì) Generator 函數(shù)的改進(jìn)知押,體現(xiàn)在以下四點(diǎn)。

(1)內(nèi)置執(zhí)行器鹃骂。
async函數(shù)自帶執(zhí)行器朗徊。也就是說,async函數(shù)的執(zhí)行偎漫,與普通函數(shù)一模一樣,調(diào)用后就會(huì)自動(dòng)執(zhí)行有缆,輸出最后結(jié)果象踊。這完全不像 Generator 函數(shù),需要調(diào)用next方法棚壁,或者用co模塊杯矩,才能真正執(zhí)行,得到最后結(jié)果袖外。

(2)更好的語(yǔ)義史隆。
async和await,比起星號(hào)和yield曼验,語(yǔ)義更清楚了泌射。async表示函數(shù)里有異步操作,await表示緊跟在后面的表達(dá)式需要等待結(jié)果鬓照。

(3)更廣的適用性熔酷。
co模塊約定,yield命令后面只能是 Thunk 函數(shù)或 Promise 對(duì)象豺裆,而async函數(shù)的await命令后面拒秘,可以是 Promise 對(duì)象和原始類型的值(一般來說是一個(gè) Promise 對(duì)象。如果不是臭猜,會(huì)被轉(zhuǎn)成一個(gè)立即resolve的 Promise 對(duì)象躺酒,類似于同步操作)。

(4)返回值是 Promise蔑歌。
async函數(shù)的返回值是 Promise 對(duì)象羹应,這比 Generator 函數(shù)的返回值是 Iterator 對(duì)象方便多了。你可以用then方法指定下一步的操作丐膝。

進(jìn)一步說量愧,async函數(shù)完全可以看作多個(gè)異步操作钾菊,包裝成的一個(gè) Promise 對(duì)象,而await命令就是內(nèi)部then命令的語(yǔ)法糖偎肃。

async 函數(shù)有多種使用形式煞烫。

// 函數(shù)聲明
async function foo() {}

// 函數(shù)表達(dá)式
const foo = async function () {};

// 對(duì)象的方法
let obj = { async foo() {} };
obj.foo().then(...)

// Class 的方法
class Storage {
  constructor() {
    this.cachePromise = caches.open('avatars');
  }

  async getAvatar(name) {
    const cache = await this.cachePromise;
    return cache.match(`/avatars/${name}.jpg`);
  }
}

const storage = new Storage();
storage.getAvatar('jake').then(…);

// 箭頭函數(shù)
const foo = async () => {};

await操作符只能在異步函數(shù) async function 中使用,它使 async 函數(shù)暫停執(zhí)行累颂,等待表達(dá)式中的 Promise解析完成后繼續(xù)執(zhí)行 async 函數(shù)并返回解決結(jié)果滞详,所以async函數(shù)內(nèi)部await后的表達(dá)式是順序執(zhí)行的。多個(gè)await命令后面的異步操作紊馏,如果不存在繼發(fā)關(guān)系料饥,最好讓它們同時(shí)觸發(fā),可以縮短程序的執(zhí)行時(shí)間朱监。

async function dbFuc(db) {
  let docs = [{}, {}, {}];
  let promises = docs.map((doc) => db.post(doc));

  let results = await Promise.all(promises);
  console.log(results);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末岸啡,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子赫编,更是在濱河造成了極大的恐慌巡蘸,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件擂送,死亡現(xiàn)場(chǎng)離奇詭異悦荒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)嘹吨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門搬味,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蟀拷,你說我怎么就攤上這事碰纬。” “怎么了匹厘?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵嘀趟,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我愈诚,道長(zhǎng)她按,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任炕柔,我火速辦了婚禮酌泰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘匕累。我一直安慰自己陵刹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布欢嘿。 她就那樣靜靜地躺著衰琐,像睡著了一般也糊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上羡宙,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天狸剃,我揣著相機(jī)與錄音,去河邊找鬼狗热。 笑死钞馁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的匿刮。 我是一名探鬼主播僧凰,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼熟丸!你這毒婦竟也來了训措?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤光羞,失蹤者是張志新(化名)和其女友劉穎隙弛,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狞山,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年叉寂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了萍启。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡屏鳍,死狀恐怖勘纯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钓瞭,我是刑警寧澤驳遵,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站山涡,受9級(jí)特大地震影響堤结,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鸭丛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一竞穷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鳞溉,春花似錦瘾带、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)朴恳。三九已至,卻和暖如春允蚣,著一層夾襖步出監(jiān)牢的瞬間于颖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工厉萝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留恍飘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓谴垫,卻偏偏與公主長(zhǎng)得像章母,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子翩剪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

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

  • 異步編程對(duì)JavaScript語(yǔ)言太重要乳怎。Javascript語(yǔ)言的執(zhí)行環(huán)境是“單線程”的,如果沒有異步編程前弯,根本...
    呼呼哥閱讀 7,311評(píng)論 5 22
  • async 函數(shù) 含義 ES2017 標(biāo)準(zhǔn)引入了 async 函數(shù)蚪缀,使得異步操作變得更加方便。 async 函數(shù)是...
    huilegezai閱讀 1,259評(píng)論 0 6
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持恕出,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券询枚,享受所有官網(wǎng)優(yōu)惠,并抽取幸運(yùn)大...
    HetfieldJoe閱讀 6,377評(píng)論 9 19
  • 弄懂js異步 講異步之前浙巫,我們必須掌握一個(gè)基礎(chǔ)知識(shí)-event-loop金蜀。 我們知道JavaScript的一大特點(diǎn)...
    DCbryant閱讀 2,710評(píng)論 0 5
  • 此時(shí),在這個(gè)蟑螂橫行的區(qū)間里的畴。 只能撐得了皮毛渊抄,卻拯救不了內(nèi)核。 可以不需要愛情丧裁,但不能沒有一廂情愿护桦。 天下也不是...
    筆鳴閱讀 205評(píng)論 0 0