Async 函數(shù)是一塊“語法糖”

原文鏈接:小哥哥小姐姐,來嘗嘗 Async 函數(shù)這塊語法糖

ES7(ECMAScript 2016)推出了Async函數(shù)(async/await),實(shí)現(xiàn)了以順序、同步代碼的編寫方式來控制異步流程,徹底解決了困擾JavaScript開發(fā)者的“回調(diào)地獄”問題。

const result = [];
// pseudo-code, ajax stand for an asynchronous request
ajax('url1', function(err, data){
    if(err) {...}
    result.push(data)
    ajax('url2', function(err, data){
        if(err) {...}
        result.push(data)
    })
})
console.log(result)

現(xiàn)在可以寫成如下同步代碼的樣式了:

async function example() {
  const r1 = await new Promise(resolve =>
    setTimeout(resolve, 500, 'slowest')
  )
  const r2 = await new Promise(resolve =>
    setTimeout(resolve, 200, 'slow')
  )
  return [r1, r2]
}

example().then(result => console.log(result))
// ['slowest', 'slow']

Async函數(shù)需要在function前面添加async關(guān)鍵字,同時內(nèi)部以await關(guān)鍵字來“阻塞”異步操作,直到異步操作返回結(jié)果,然后再繼續(xù)執(zhí)行。

當(dāng)前JavaScript編程主要是異步編程

  • 當(dāng)前JavaScript編程主要是異步編程。為什么這么說呢?網(wǎng)頁或Web開發(fā)最早從2005年Ajax流行開始,逐步向重交互時代邁進(jìn)。
  • 特別是SPA(Single Page Application,單頁應(yīng)用)流行之后,一度有人提出“Web頁面要轉(zhuǎn)向Web應(yīng)用,而且要媲美原生應(yīng)用”。
  • 如今在前端開發(fā)組件化的背景下催生的Angular、React和Vue,都是SPA進(jìn)一步演化的結(jié)果。
  • 頁面在首次加載過程中,與JavaScript相關(guān)的主要任務(wù)就是加載基礎(chǔ)運(yùn)行庫和擴(kuò)展庫(包括給低版本瀏覽器打補(bǔ)丁的腳本),然后初始化和設(shè)置頁面的狀態(tài)。
  • 目前JavaScript編程最大的應(yīng)用是Web交互,而Web交互的核心就是異步邏輯。

ES6之前JavaScript中控制異步流程的手段只有事件和回調(diào)。比如下面的示例展示了通過原生XMLHttpRequest對象發(fā)送異步請求,然后給onloadonerror事件分別注冊成功和錯誤處理函數(shù):

var req = new XMLHttpRequest();
req.open('GET', url);

req.onload = function () {
    if (req.status == 200) {
        processData(req.response);
    } 
};

req.onerror = function () {
    console.log('Network Error');
};

req.send();

事件和回調(diào)有很多問題,主要是它們只適用于簡單的情況。邏輯一復(fù)雜,代碼的編寫和維護(hù)成本就成倍上升。比如,大家熟知的“回調(diào)地獄”。更重要的是,回調(diào)模式的異步本質(zhì)與人類同步、順序的思維模式是相悖的。
為了應(yīng)對越來越復(fù)雜的異步編程需求,ES6推出了解決上述問題的Promise。

Promise

Promise,人們普遍的理解就是:“Promise是一個未來值的占位符”。也就是說,從語義上講,一個Promise對象代表一個對未來值的“承諾”(promise),這個承諾將來如果“兌現(xiàn)”(fulfill),就會“解決”(resolve)為一個有意義的數(shù)據(jù);如果“拒絕”(reject),就會“解決”為一個“拒絕理由”(rejection reason),就是一個錯誤消息。
Promise對象的狀態(tài)很簡單,一生下來的狀態(tài)是pending(待定),將來兌現(xiàn)了,狀態(tài)變成fulfilled;拒絕了,狀態(tài)變成rejectedfulfilledrejected顯然是一種“確定”(settled)狀態(tài)。以上狀態(tài)轉(zhuǎn)換是不可逆的

Promise對象的狀態(tài).png

以下是通過Prmoise(executor)構(gòu)造函數(shù)創(chuàng)建Promise實(shí)例的詳細(xì)過程:要傳入一個“執(zhí)行函數(shù)”(executor),這個執(zhí)行函數(shù)又接收兩個參數(shù)“解決函數(shù)”(resolver)和“拒絕函數(shù)”(rejector),代碼中分別對應(yīng)變量resolvereject,作用分別是將新建對象的狀態(tài)由pending改為fulfilledrejected,同時返回“兌現(xiàn)值”(fulfillment)和“拒絕理由”(rejection)。當(dāng)然,resolvereject都是在異步操作的回調(diào)中調(diào)用的。調(diào)用之后,運(yùn)行時環(huán)境(瀏覽器引擎或Node.js的libuv)中的事件循環(huán)調(diào)度機(jī)制會把與之相關(guān)的反應(yīng)函數(shù)——兌現(xiàn)反應(yīng)函數(shù)或拒絕反應(yīng)函數(shù)以及相關(guān)的參數(shù)添加到“微任務(wù)”隊(duì)列,以便下一次“循檢”(tick)時調(diào)度到JavaScript線程去執(zhí)行。

Prmoise(executor).png

如前所述,Promise對象的狀態(tài)由pending變成fulfilled,就會執(zhí)行“兌現(xiàn)反應(yīng)函數(shù)”(fulfillment reaction);而變成rejected,就會執(zhí)行“拒絕反應(yīng)函數(shù)”(rejection reaction)。如下例所示,常規(guī)的方式是通過p.then()注冊兌現(xiàn)函數(shù),通過p.catch()注冊拒絕函數(shù):

p.then(res => { // 兌現(xiàn)反應(yīng)函數(shù)
  // res === 'random success'
})
p.catch(err => { // 拒絕反應(yīng)函數(shù)
  // err === 'random failure'
})

當(dāng)然還有非常規(guī)的方式,而且有時候非常規(guī)方式可能更好用:

// 通過一個.then()方法同時注冊兌現(xiàn)和拒絕函數(shù)
p.then(
  res => {
    // handle response
  },
  err => {
    // handle error
  }
)
// 通過.then()方法只注冊一個函數(shù):兌現(xiàn)函數(shù)
p.then(res => {
  // handle response
})
// 通過.then()方法只傳入拒絕函數(shù),兌現(xiàn)函數(shù)的位置傳null
p.then(null, err => {
  // handle error
})

關(guān)于Promise就這樣吧。ES6除了Promise,還推出了Iterator(迭代器)和Generator(生成器),于是就有成就Async函數(shù)的PIG組合。(詳見原文鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • title: promise總結(jié) 總結(jié)在前 前言 下文類似 Promise#then、Promise#resolv...
    JyLie閱讀 12,418評論 1 21
  • Promise 對象 Promise 的含義 Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函...
    neromous閱讀 8,835評論 1 56
  • 你不知道JS:異步 第三章:Promises 接上篇3-1 錯誤處理(Error Handling) 在異步編程中...
    purple_force閱讀 1,497評論 0 2
  • 異步編程對JavaScript語言太重要。Javascript語言的執(zhí)行環(huán)境是“單線程”的,如果沒有異步編程,根本...
    呼呼哥閱讀 7,405評論 5 22
  • 引用: 供奉的遺像是牽引家人回家的通道,駐留的記憶是保持亡靈存續(xù)的神力,熱鬧的音樂是喚醒思念啟封的藥引。我為你寫了...
    同小姐77閱讀 336評論 0 1

友情鏈接更多精彩內(nèi)容