溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

ES7中利用Await減少回調(diào)嵌套的方法詳解

發(fā)布時(shí)間:2020-10-25 00:02:42 來(lái)源:腳本之家 閱讀:162 作者:李銀城 欄目:web開(kāi)發(fā)

前言

我們知道javascript是沒(méi)辦法阻塞的,所有的等待只能通過(guò)回調(diào)來(lái)完成,這就造成了回調(diào)嵌套的問(wèn)題,導(dǎo)致代碼亂到爆,這時(shí)候Await就有用處了。

對(duì)于await的底層機(jī)制這里就不詳述了,以免將文章的篇幅拖的很長(zhǎng),需要的朋友們可以參考這篇文章:https://www.jb51.net/article/123257.htm,下面開(kāi)始本文的正式內(nèi)容。

利用Await減少回調(diào)嵌套

我們大家在開(kāi)發(fā)的時(shí)候,有時(shí)候需要發(fā)很多請(qǐng)求,然后經(jīng)常會(huì)面臨嵌套回調(diào)的問(wèn)題,即在一個(gè)回調(diào)里面又嵌了一個(gè)回調(diào),導(dǎo)致代碼層層縮進(jìn)得很厲害。

如下代碼所示:

ajax({
 url: "/list",
 type: "GET",
 success: function(data) {
 appendToDOM(data);
 ajax({
 url: "/update",
 type: "POST",
 success: function(data) {
 util.toast("Success!");
 })
 });
 }
});

這樣的代碼看起來(lái)有點(diǎn)吃力,這種異步回調(diào)通??梢杂肞romise優(yōu)化一下,可以把上面代碼改成:

new Promise(resolve => {
 ajax({
 url: "/list",
 type: "GET",
 success: data => resolve(data);
 })
}).then(data => {
 appendToDOM(data);
 ajax({
 url: "/update",
 type: "POST",
 success: function(data) {
 util.toast("Successfully!");
 }) 
 }); 
});

Promise提供了一個(gè)resolve,方便通知什么時(shí)候異步結(jié)束了,不過(guò)本質(zhì)還是一樣的,還是使用回調(diào),只是這個(gè)回調(diào)放在了then里面。

當(dāng)需要獲取多次異步數(shù)據(jù)的時(shí)候,可以使用Promise.all解決:

let orderPromise = new Promise(resolve => {
 ajax("/order", "GET", data => resolve(data));
});
let userPromise = new Promise(resolve => {
 ajax("/user", "GET", data => resolve(data));
});

Promise.all([orderPromise, userPromise]).then(values => {
 let order = values[0],
 user = values[1];
});

但是這里也是使用了回調(diào),有沒(méi)有比較優(yōu)雅的解決方式呢?

ES7的await/async可以讓異步回調(diào)的寫(xiě)法跟寫(xiě)同步代碼一樣。第一個(gè)嵌套回調(diào)的例子可以用await改成下面的代碼:

// 使用await獲取異步數(shù)據(jù)
let leadList = await new Promise(resolve => {
 ajax({
 url: "/list",
 type: "GET",
 success: data => resolve(data);
 });
});

// await讓代碼很自然地像瀑布流一樣寫(xiě)下來(lái) 
appendToDom(leadList);
ajax({
 url: "/update",
 type: "POST",
 success: () => util.toast("Successfully");
});

Await讓代碼可以像瀑布流一樣很自然地寫(xiě)下來(lái)。

第二個(gè)例子:獲取多次異步數(shù)據(jù),可以改成這樣:

let order = await new Promise(
 resolve => ajax("/order", data => resovle(data))),

 user = await new Promise(
 resolve => ajax("/user", data => resolve(data)));

// do sth. with order/user

這種寫(xiě)法就好像從本地獲取數(shù)據(jù)一樣,就不用套回調(diào)函數(shù)了。

Await除了用在發(fā)請(qǐng)求之外,還適用于其它異步場(chǎng)景,例如我在創(chuàng)建訂單前先彈一個(gè)小框詢(xún)問(wèn)用戶(hù)是要?jiǎng)?chuàng)建哪種類(lèi)型的訂單,然后再?gòu)椌唧w的設(shè)置訂單的框,所以按正常思路這里需要傳遞一個(gè)按鈕回調(diào)的點(diǎn)擊函數(shù),如下圖所示:

ES7中利用Await減少回調(diào)嵌套的方法詳解

但其實(shí)可以使用await解決,如下代碼所示:

let quoteHandler = require("./quote");
// 彈出框詢(xún)問(wèn)用戶(hù)并得到用戶(hù)的選擇
let createType = await quoteHandler.confirmCreate();

quote里面返回一個(gè)Promise,監(jiān)聽(tīng)點(diǎn)擊事件,并傳遞createType:

let quoteHandler = {
 confirmCreate: function(){
 dialog.showDialog({
 contentTpl: tpl,
 className: "confirm-create-quote"
 });
 let $quoteDialog = $(".confirm-create-quote form")[0];
 return new Promise(resolve => {
 $(form.submit).on("click", function(event){
 resolve(form.createType.value);
 });
 });
 }

}

這樣外部調(diào)用者就可以使用await,而不用傳遞一個(gè)點(diǎn)擊事件的回調(diào)函數(shù)了。

但是需要注意的是await的一次性執(zhí)行特點(diǎn)。相對(duì)于回調(diào)函數(shù)來(lái)說(shuō),await的執(zhí)行是一次性的,例如監(jiān)聽(tīng)點(diǎn)擊事件,然后使用await,那么點(diǎn)擊事件只會(huì)執(zhí)行一次,因?yàn)榇a從上往下執(zhí)行完了,所以當(dāng)希望點(diǎn)擊之后出錯(cuò)了還能繼續(xù)修改和提交就不能使用await,另外使用await獲取異步數(shù)據(jù),如果出錯(cuò)了,那么成功的resolve就不會(huì)執(zhí)行,后續(xù)的代碼也不會(huì)執(zhí)行,所以請(qǐng)求出錯(cuò)的時(shí)候基本邏輯不會(huì)有問(wèn)題。

要在babel里面使用await,需要:

(1)安裝一個(gè)Node包

npm install --save-dev babel-plugin-transform-async-to-generator

(2)在工程的根目錄添加一個(gè).babelrc文件,內(nèi)容為:

{
 "plugins": ["transform-async-to-generator"]
}

(3)使用的時(shí)候先引入一個(gè)模塊

require("babel-polyfill");

然后就可以愉快地使用ES7的await了。

使用await的函數(shù)前面需要加上async關(guān)鍵字,如下代碼:

async showOrderDialog() {
 // 獲取創(chuàng)建類(lèi)型
 let createType = await quoteHandler.confirmCreate();

 // 獲取老訂單數(shù)據(jù) 
 let orderInfo = await orderHandler.getOrderData();
}

我們?cè)倥e一個(gè)例子:使用await實(shí)現(xiàn)JS版的sleep函數(shù),因?yàn)樵菦](méi)有提供線程休眠函數(shù)的,如下代碼所示:

function sleep (time) {
 return new Promise(resolve => 
 setTimeout(() => resolve(), time));
}

async function start () {
 await sleep(1000);
}

start();

babel的await實(shí)現(xiàn)是轉(zhuǎn)成了ES6的generator,如下關(guān)鍵代碼:

while (1) {
 switch (_context.prev = _context.next) {
 case 0:
 _context.next = 2;
 // sleep返回一個(gè)Promise對(duì)象
 return sleep(1000);

 case 2:
 case "end": 
 return _context.stop();
 }
}

而babel的generator也是要用ES5實(shí)現(xiàn)的,什么是generator呢?如下圖所示:

ES7中利用Await減少回調(diào)嵌套的方法詳解

生成器用function*定義,每次執(zhí)行生成器的next函數(shù)的時(shí)候會(huì)返回當(dāng)前生成器里用yield返回的值,然后生成器的迭代器往后走一步,直到所有yield完了。

有興趣的可以繼續(xù)研究babel是如何把ES7轉(zhuǎn)成ES5的,據(jù)說(shuō)原生的實(shí)現(xiàn)還是直接基于Promise.

使用await還有一個(gè)好處,可以直接try-catch捕獲異步過(guò)程拋出的異常,因?yàn)槲覀兪遣荒苤苯硬东@異步回調(diào)里面的異常的,如下代碼:

let quoteHandler = {
 confirmCreate: function(){
 $(form.submit).on("click", function(event){
 // 這里會(huì)拋undefined異常:訪問(wèn)了undefined的value屬性
 callback(form.notFoundInput.value);
 });
 }
}

try {
 // 這里無(wú)法捕獲到異常
 quoteHandler.confirmCreate();
} catch (e) {

}

上面的try-catch是沒(méi)有辦法捕獲到異常的,因?yàn)閠ry里的代碼已經(jīng)執(zhí)行完了,在它執(zhí)行的過(guò)程中并沒(méi)有異常,因此無(wú)法在這里捕獲,如果使用Promise的話一般是使用Promise鏈的catch:

let quoteHandler = {
 confirmCreate: function(){
 return new Promise(resolve => {
 $(form.submit).on("click", function(event){
 // 這里會(huì)拋undefined異常:訪問(wèn)了undefined的value屬性
 resolve(form.notFoundInput.value);
 });
 });
 }
}

quoteHandler.confirmCreate().then(createType => {

}).catch(e => {
 // 這里能捕獲異常
});

而使用await,我們可以直接用同步的catch,就好像它真的變成同步執(zhí)行了:

try {
 createType = await quoteHandler.confirmCreate("order");
}catch(e){
 console.log(e);
 return;
}

總之使用await讓代碼少寫(xiě)了很多嵌套,很方便的邏輯處理,縱享絲滑。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)億速云的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI