国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JavaScript 工作原理之四-事件循環及異步編程的出現和 5 種更好的 async/await

maochunguang / 1969人閱讀

摘要:函數會在之后的某個時刻觸發事件定時器。事件循環中的這樣一次遍歷被稱為一個。執行完畢并出棧。當定時器過期,宿主環境會把回調函數添加至事件循環隊列中,然后,在未來的某個取出并執行該事件。

原文請查閱這里,略有改動。

本系列持續更新中,Github 地址請查閱這里。

這是 JavaScript 工作原理的第四章。

現在,我們將會通過回顧單線程環境下編程的弊端及如何克服這些困難以創建令人驚嘆的 JavaScript 交互界面來展開第一篇文章。老規矩,我們將會在本章末尾分享 5 條利用 async/await 編寫更簡潔代碼的小技巧。

單線程的局限性

在第一篇文章開頭,我們考慮了一個問題即當調用棧中含有需要長時間運行的函數調用的時候會發生什么。

譬如,試想下,在瀏覽器中運行著一個復雜的圖片轉化算法。

恰好此時調用棧中有函數需要執行,此時瀏覽器將會被阻塞,它不能夠做其它任何事情。這意味著,瀏覽器會沒有響應,不能夠進行渲染和運行其它代碼。這將會帶來問題-程序界面將不再高效和令人愉悅。

程序沒有響應。

在某些情況下,這或許沒什么大不了的。但是,這可能會造成更加嚴重的問題。一旦瀏覽器在調用棧中同時運行太多的任務的時候,瀏覽器會很長時間停止響應。到了那個時候,大多數瀏覽器會拋出一個錯誤,詢問你是否關閉網頁。

這很丑陋且它完全摧毀了程序的用戶體驗。

JavaScript 程序組件

你可能會在單一的 .js 文件中書寫 JavaScript 程序,但是程序是由多個代碼塊組成的,當前只有一個代碼塊在運行,其它代碼塊將在隨后運行。最常見的塊狀單元是函數。

大多數 JavaScript 菜鳥有可能需要理解的問題即之后運行表示的是并不是必須嚴格且立即在現在之后執行。換句話說即,根據定義,現在不能夠運行完畢的任務將會異步完成,這樣你就不會不經意間遇到以上提及的 UI 阻塞行為。

看下如下代碼:

// ajax 為一個庫提供的任意 ajax 函數
var response = ajax("https://example.com/api");

console.log(response);
// `response` 將不會有數據返回

可能你已經知道標準的 ajax 請求不會完全同步執行完畢,意即在代碼運行階段,ajax(..) 函數不會返回任何值給 response 變量。

獲得異步函數返回值的一個簡單方法是使用回調函數。

ajax("https://example.com/api", function(response) {
    console.log(response); // `response` 現在有值
});

只是要注意一點:即使可以也永遠不要發起同步 ajax 請求。如果發起同步 ajax 請求,JavaScript 程序的 UI 將會被阻塞-用戶不能夠點擊,輸入數據,跳轉或者滾動。這將會凍結任何用戶交互體驗。這是非常糟糕。

以下示例代碼,但請別這樣做,這會毀掉網頁:

// 假設你使用 jQuery
jQuery.ajax({
    url: "https://api.example.com/endpoint",
    success: function(response) {
        // 成功回調.
    },
    async: false // 同步
});

我們以 Ajax 請求為例。你可以異步執行任意代碼。

你可以使用 setTimeout(callback, milliseconds) 函數來異步執行代碼。setTimeout 函數會在之后的某個時刻觸發事件(定時器)。如下代碼:

function first() {
    console.log("first");
}
function second() {
    console.log("second");
}
function third() {
    console.log("third");
}
first();
setTimeout(second, 1000); // 1 秒后調用 second 函數
third();

控制臺輸出如下:

first
third
second
事件循環詳解

我們將會以一個有些讓人費解的問題開始-盡管允許異步執行 JavaScript 代碼(比如之前討論的 setTimetout),但是直到 ES6,實際上 JavaScript 本身并沒有集成任何直接的異步編程概念。JavaScript 引擎只允許在任意時刻執行單個的程序片段。

可以查看之前的文章來了解 JavaScript 引擎的工作原理。

那么, JS 引擎是如何執行程序片段的呢?實際上,JS 引擎并不是隔離運行的-它運行在一個宿主環境中,對大多數開發者來說是典型的 web 瀏覽器或者 Node.js。實際上,現在 JavaScript 廣泛應用于從機器到電燈泡的各種設備之中。每個設備代表了 JS 引擎的不同類型的宿主環境。

所有宿主環境都含有一個被稱為事件循環的內置機制,隨著時間的推移,事件循環會執行程序中多個代碼片段,每次都會調用 JS 引擎。

這意味著 JS 引擎只是任意 JS 代碼的按需執行環境。這是一個封閉的環境,在其中進行事件的調度(運行JS 代碼)。

所以,打個比方,當 JavaScript 程序發起 Ajax 請求來從服務器獲得數據,你在回調函數中書寫 "response" 代碼,JS 引擎會告訴宿主環境:

"嘿,我現在要掛起執行了,現在當你完成網絡請求的時候且返回了數據,請執行回調函數。"

之后瀏覽器會監聽從網絡中返回的數據,當有數據返回的時候,它會通過把回調函數插入事件循環以便調度執行。

讓我們看下如下圖示:

你可以在之前的文章中閱讀更多關于動態內存管理和調用棧的信息。

什么是網頁 API ?本質上,你沒有權限訪問這些線程,你只能夠調用它們。它們是瀏覽器自帶的,且可以在瀏覽器中進行并發操作。如果你是個 Node.js 開發者,這些是 C++ APIs。

說了那么多,事件循環到底是啥?

事件循環只有一項簡單的工作-監測調用棧和回調隊列。如果調用棧是空的,它會從回調隊列中取得第一個事件然后入棧,并有效地執行該事件。

事件循環中的這樣一次遍歷被稱為一個 tick。每個事件就是一個回調函數。

console.log("Hi");
setTimeout(function cb1() { 
    console.log("cb1");
}, 5000);
console.log("Bye");

讓我們執行這段代碼,然后看看會發生什么:

1.空狀態。瀏覽器控制臺是空的,調用棧也是空的。

2.console.log("Hi") 入棧。

3.執行 console.log("Hi")

4.console.log("Hi") 出棧

setTimeout(function cb1() { ... }) 入棧。

6.執行 setTimeout(function cb1() { ... }),瀏覽器創建定時器作為網頁 API 的一部分并將會為你處理倒計時。

7.setTimeout(function cb1() { ... }) 執行完畢并出棧。

8.console.log("Bye") 入棧。

9.執行 console.log("Bye")

10.console.log("Bye") 出棧。

11.至少 5 秒之后,定時器結束運行并把 cb1 回調添加到回調隊列。

12.事件循環從回調隊列中獲得 cb1 函數并且將其入棧。

13.運行 cb1 函數并將 console.log("cb1") 入棧。

14.執行 console.log("cb1")

15.console.log("cb1") 出棧。

16.cb1 出棧

錄像快速回放:

令人感興趣的是,ES6 規定事件循環如何工作的,這意味著從技術上講,它在 JS 引擎負責的范圍之內,而 JS 引擎將不再只是扮演著宿主環境的角色。ES6 中 Promise 的出現是導致改變的主要原因之一,因為 ES6 要求有權限直接細粒度地控制事件循環隊列中的調度操作(之后會深入探討)。

setTimeout(…) 工作原理

需要注意的是 setTimeout(…) 并沒有自動把回調添加到事件循環隊列。它創建了一個定時器。當定時器過期,宿主環境會把回調函數添加至事件循環隊列中,然后,在未來的某個 tick 取出并執行該事件。查看如下代碼:

setTimeout(myCallback, 1000);

這并不意味著 1 秒之后會執行 myCallback 回調而是在 1 秒后將其添加到回調隊列。然而,該隊列有可能在之前就添加了其它的事件-所以回調就會被阻塞。

有相當一部分的文章和教程開始會建議你使用 setTimeout(callback, 0) 來書寫 JavaScript 異步代碼。那么,現在你明白了事件循環和 setTimeout 的原理:調用 setTimeout 把其第二個參數設置為 0 表示延遲執行回調直到調用棧被清空。

查看如下代碼:

console.log("Hi");
setTimeout(function() {
    console.log("callback");
}, 0);
console.log("Bye");

雖然定時時間設定為 0, 但是控制臺中的結果將會如下顯示:

Hi
Bye
callback
ES6 作業概念

ES6 介紹了一個被稱為『作業隊列』的概念。它位于事件循環隊列的頂部。你極有可能在處理 Promises(之后會介紹) 的異步行為的時候無意間接觸到這一概念。

現在我們將會接觸這個概念,以便當討論 Promises 的異步行為之后,理解如何調度和處理這些行為。

像這樣想象一下:作業隊列是附加于事件循環隊列中每個 tick 末尾的隊列。事件循環的一個 tick 所產生的某些異步操作不會導致添加全新的事件到事件循環隊列中,但是反而會在當前 tick 的作業隊列末尾添加一個作業項。

這意味著,你可以添加延時運行其它功能并且你可以確保它會在其它任何功能之前立刻執行。

一個作業也可以在同一隊列末尾添加更多的作業。理論上講,存在著作業循環的可能性(比如作業不停地添加其它作業)。

為了無限循環,就會饑餓程序所需要的資源直到下一個事件循環 tick。從概念上講,這類似于在代碼里面書寫耗時或者死循環(類似 while(true))。

作業是有些類似于 setTimeout(callback, 0) 小技巧,但是是以這樣的方式實現的,它們擁有明確定義和有保證的執行順序:之后且盡快地執行。

回調

正如你已知的那樣,回調函數是 JavaScript 程序中用來表示和進行異步操作的最常見方法。的確,回調是 JavaScript 語言中最為重要的異步模式。無數的 JS 程序,甚至非常復雜的那些,都是建立在回調函數之上的。

回調并不是沒有缺點。許多開發者試圖找到更好的異步模式。然而,如果你不理解底層的原理而想要高效地使用任何抽象化的語法這是不可能的。

在接下來的章節中,我們將會深入探究這些抽象語法并理解更復雜的異步模式的必要性。

嵌套回調

查看以下示例:

listen("click", function (e){
    setTimeout(function(){
        ajax("https://api.example.com/endpoint", function (text){
            if (text == "hello") {
            doSomething();
        }
        else if (text == "world") {
            doSomethingElse();
            }
        });
    }, 500);
});

我們有三個鏈式嵌套函數,每個函數代表一個異步操作。

這類代碼通常被稱為『回調地獄』。但是,實際上『回調地獄』和代碼嵌套及縮進沒有任何關系。這是一個更加深刻的問題。

首先,我們監聽點擊事件,然后,等待定時器執行,最后等待 Ajax 返回數據,在 Ajax 返回數據的時候,可以重復執行這一過程。

乍一眼看上去,可以上把以上具有異步特性的代碼拆分為按步驟執行的代碼,如下所示:

listen("click", function (e) {
    // ..
});

之后:

setTimeout(function(){
    // ..
}, 500);

再后來:

ajax("https://api.example.com/endpoint", function (text){
    // ..
});

最后:

if (text == "hello") {
    doSomething();
}
else if (text == "world") {
    doSomethingElse();
}

因此,以這樣順序執行的方式來表示異步代碼看起來一氣呵成,應該有這樣的方法吧?

Promises

查看如下代碼:

var x = 1;
var y = 2;
console.log(x + y);

這個很直觀:計算出 x 和 y 的值然后在控制臺打印出來。但是,如果 x 或者 y 的初始值是不存在的且不確定的呢?假設,在表達式中使用 x 和 y 之前,我們需要從服務器得到 x 和 y 的值。想象下,我們擁有函數 loadXloadY 分別從服務器獲取 x 和 y 的值。然后,一旦獲得 xy 的值,就可以使用 sum 函數計算出和值。

類似如下這樣:

function sum(getX, getY, callback) {
    var x, y;
    getX(function(result) {
        x = result;
        if (y !== undefined) {
            callback(x + y);
        }
    });
    getY(function(result) {
        y = result;
        if (x !== undefined) {
            callback(x + y);
        }
    });
}
// 同步或異步獲取 `x` 值的函數
function fetchX() {
    // ..
}


// 同步或異步獲取 `y` 值的函數
function fetchY() {
    // ..
}

sum(fetchX, fetchY, function(result) {
    console.log(result);
});

這里需要記住的一點是-在代碼片段中,xy 是未來值,我們用 sum(..)(從外部)來計算和值,但是并沒有關注 xy 是否馬上同時有值。

當然嘍,這個粗糙的基于回調的技術還有很多需要改進的地方。這只是理解推出未來值而不用擔心何時有返回值的好處的一小步。

Promise 值

讓我們簡略地看一下如何用 Promises 來表示 x+y

function sum(xPromise, yPromise) {
    // `Promise.all([ .. ])` 包含一組 Promise,
    // 并返回一個新的 Promise 來等待所有 Promise 執行完畢
    return Promise.all([xPromise, yPromise])

    // 當新 Promise 解析完畢,就可以同時獲得 `x` 和 `y` 的值并相加。
    .then(function(values){
        // `values` 是之前解析 promises 返回的消息數組
        return values[0] + values[1];
    } );
}

// `fetchX()` and `fetchY()` 返回 promise 來取得各自的返回值,這些值返回是無時序的。
sum(fetchX(), fetchY())

// 獲得一個計算兩個數和值的 promise,現在,就可以鏈式調用 `then(...)` 來處理返回的 promise。
.then(function(sum){
    console.log(sum);
});

以上代碼片段含有兩種層次的 Promise。

fetchX()fetchY() 都是直接調用,它們的返回值(promises!)都被傳入 sum(…) 作為參數。雖然這些 promises 的 返回值也許會在現在或之后返回,但是無論如何每個 promise 都具有相同的異步行為。我們可以的推算 xy 是與時間無關的值。暫時稱他們為未來值。

第二層次的 promise 是由 sum(…) (通過 Promise.all([ ... ]))所創建和返回的,然后通過調用 then(…) 來等待 promise 的返回值。當 sum(…) 運行結束,返回 sum 未來值然后就可以打印出來。我們在 sum(…) 內部隱藏了等待未來值 xy 的邏輯。

注意:sum(…) 內部,Promise.all([ … ])創建了一個 promise(在等待 promiseXpromiseY 解析之后)。鏈式調用 .then(…) 創建了另一個 promise,該 promise 會由代碼 values[0] + values[1] 立刻進行解析(返回相加結果)。因此,在代碼片段的末尾即 sum(…) 的末尾鏈式調用 then(…)-實際上是在操作第二個返回的 promise 而不是第一個由 Promise.all([ ... ]) 創建返回的 promise。同樣地,雖然我們沒有在第二個then(…) 之后進行鏈式調用,但是它也創建了另一個 promise,我們可以選擇觀察/使用該 promise。我們將會在本章的隨后內容中進行詳細地探討 promise 的鏈式調用相關。

在 Promises 中,實際上 then(…) 函數可以傳入兩個函數作為參數,第一個函數是成功函數,第二個是失敗函數。

sum(fetchX(), fetchY())
.then(
    // 成功句柄
    function(sum) {
        console.log( sum );
    },
    // 拒絕句柄
    function(err) {
        console.error( err ); // bummer!
    }
);

當獲取 x 或者 y 出現錯誤或者計算和值的時候出現錯誤,sum(…) 返回的 promise 將會失敗,傳入 then(…) 作為第二個參數的回調錯誤處理程序將會接收 promise 的返回值。

因為 Promise 封裝了時間相關的狀態-等待外部的成功或者失敗的返回值,Promise 本身是與時間無關的,這樣就能夠以可預測的方式組成(合并) Promise 而不用關心時序或者返回結果。

除此之外,一旦 Promise 解析完成,它就會一直保持不可變的狀態且可以被隨意觀察。

鏈式調用 promise 真的很管用:

function delay(time) {
    return new Promise(function(resolve, reject){
        setTimeout(resolve, time);
    });
}

delay(1000)
.then(function(){
    console.log("after 1000ms");
    return delay(2000);
})
.then(function(){
    console.log("after another 2000ms");
})
.then(function(){
    console.log("step 4 (next Job)");
    return delay(5000);
})
// ...

調用 delay(2000) 創建一個將在 2 秒后返回成功的 promise,然后,從第一個 then(…) 的成功回調函數中返回該 promise,這會導致第二個 then(…) 返回的 promise 等待 2 秒后返回成功的 promise。

Note:因為一個 promise 一旦解析其狀態就不可以從外部改變,由于它的狀態不可以被隨意修改,所以可以安全地把狀態值隨意分發給任意第三方。當涉及多方觀察 Promise 的返回結果時候更是如此。一方影響另一方觀察 Promise 返回結果的能力是不可能。不可變性聽起來像是個晦澀的科學課題,但是,實際上這是 Promise 最根本和重要的方面,你得好好研究研究。

Promise 使用時機

Promise 的一個重要細節即確定某些值是否是真正的 Promise。換句話說,這個值是否具有 Promise 的行為。

我們知道可以利用 new Promise(…) 語法來創建 Promise,然后,你會認為使用 p instanceof Promise 來檢測某個對象是否是 Promise 類的實例。然而,并不全然如此。

主要的原因在于你可以從另一個瀏覽器窗口(比如 iframe)獲得 Promise 實例,iframe 中的 Promise 不同于當前瀏覽器窗口或框架中的 Promise,因此,會導致檢測 Promise 實例失敗。

除此之外,庫或框架或許會選擇使用自身自帶的 Promise 而不是原生的 ES6 實現的 Promise。實際工作中,你可以使用庫自帶的 Promise 來兼容不支持 Promise 的老版本瀏覽器。

異常捕獲

如果在創建 Promise 或者是在觀察解析 Promise 返回結果的任意時刻,遇到了諸如 TypeError 或者 ReferenceError 的 JavaScript 錯誤異常,這個異常會被捕獲進而強制 Promise 為失敗狀態。

比如:

var p = new Promise(function(resolve, reject){
    foo.bar();      // `foo` 未定義,產生錯誤!
    resolve(374); // 永不執行 :(
});

p.then(
    function fulfilled(){
        // 永不執行 :(
    },
    function rejected(err){
        // `err` 會是一個 `TypeError` 異常對象
         // 由于 `foo.bar()` 代碼行.
    }
);

但是,如果 Promise 成功解析了而在成功解析的監聽函數(then(…) 注冊回調)中拋出 JS 運行錯誤會怎么樣?仍然可以捕捉到該異常,但或許你會發現處理這些異常的方式有些讓人奇怪。直到深入理解其中原理:

var p = new Promise( function(resolve,reject){
    resolve(374);
});

p.then(function fulfilled(message){
    foo.bar();
    console.log(message);   // 永不執行
},
    function rejected(err){
        // 永不執行
    }
);

看起來 foo.bar() 拋出的錯誤異常真的被捕獲到了。然而,事實上并沒有。然而,深入理解你會發現我們沒有監測到其中一些錯誤。p.then(…) 調用本身返回另一個 promise,該 promise 會返回 TypeError 類型的異常失敗信息。

拓展一下以上的說明,這是原文沒有的。

var p = new Promise( function(resolve,reject){
    resolve(374);
});

p.then(function fulfilled(message){
    foo.bar();
    console.log(message);   // 永不執行
},
    function rejected(err){
        // 永不執行
    }
).then(
function() {},
function(err) { console.log("err", err);}
);

如上代碼所示就可以真正捕獲到 promise 成功解析回調函數里面的代碼錯誤。

處理未捕獲的異常

有其它許多據說更好的處理異常的技巧。

普遍的做法是為 Promises 添加 done(..) 回調,本質上這會標記 promise 鏈的狀態為 "done."。done(…) 并不會創建和返回 promise,因此,當不存在鏈式 promise 的時候,傳入 done(..) 的回調顯然并不會拋出錯誤。

和未捕獲的錯誤狀況一樣:任何在 done(..) 失敗處理函數中的異常都將會被拋出為全局錯誤(基本上是在開發者控制臺)。

var p = Promise.resolve(374);

p.then(function fulfilled(msg){
    // 數字沒有字符類的函數,所以會報錯
    console.log(msg.toLowerCase());
})
.done(null, function() {
    // 若發生錯誤,將會拋出全局錯誤
});
ES8 中的 Async/await

JavaScript ES8 中介紹了 async/await,這使得處理 Promises 更加地容易。我們將會簡要介紹 async/await 的所有可能姿勢并利用其來書寫異步代碼。

那么,讓我們瞧瞧 async/await 工作原理。

使用 async 函數定義一個異步函數。該函數會返回異步函數對象。AsyncFunction 對象表示在異步函數中運行其內部代碼。

當調用異步函數的時候,它會返回一個 Promise。異步函數返回值并非 Promise,在函數過程中會自動創建一個 Promise 并使用函數的返回值來解析該 Promise。當 async 函數拋出異常,Promise 失敗回調會獲取拋出的異常值。

async 函數可以包含一個 await 表達式,這樣就可以暫停函數的執行來等待傳入的 Promise 的返回結果,之后重啟異步函數的執行并返回解析值。

你可以把 JavaScript 中的 Promise 看作 Java 中的 FutureC# 中的 Task。

async/await 本意是用來簡化 promises 的使用。

看下如下代碼:

// 標準 JavaScript 函數
function getNumber1() {
    return Promise.resolve("374");
}
// 和 getNumber1 一樣
async function getNumber2() {
    return 374;
}

類似地,拋出異常的函數等價于返回失敗的 promises。

function f1() {
    return Promise.reject("Some error");
}
async function f2() {
    throw "Some error";
}

await 關鍵字只能在 async 函數中使用并且允許你同步等待 Promise。如果在 async 函數外使用 promises,我們仍然必須使用 then 回調。

async function loadData() {
    // `rp` 是個發起 promise 的函數。
    var promise1 = rp("https://api.example.com/endpoint1");
    var promise2 = rp("https://api.example.com/endpoint2");
   
    // 現在,并發請求兩個 promise,現在我們必須等待它們結束運行。
    var response1 = await promise1;
    var response2 = await promise2;
    return response1 + " " + response2;
}
// 因為不再使用 `async function`,所以必須使用 `then`。
loadData().then(() => console.log("Done"));

你也可以使用異步函數表達式來定義異步函數。異步函數表達式擁有和異步函數語句相近的語法。異步函數表達式和異步函數語句的主要區別在于函數名,異步函數表達式可以忽略函數名來創建匿名函數。異步函數表達式可以被用作 IIFE(立即執行函數表達式),可以在定義的時候立即運行。

像這樣:

var loadData = async function() {
    // `rp` 是個發起 promise 的函數。
    var promise1 = rp("https://api.example.com/endpoint1");
    var promise2 = rp("https://api.example.com/endpoint2");
   
    // 現在,并發請求兩個 promise,現在我們必須等待它們結束運行。
    var response1 = await promise1;
    var response2 = await promise2;
    return response1 + " " + response2;
}

更為重要的是,所有的主流瀏覽器都支持 async/await。

如果該兼容性不符合你的需求,你可以使用諸如 Babel 和 TypeScript 的 JS 轉譯器來轉換為自己需要的兼容程度。

最后要說的是,不要盲目地使用最新的技術來寫異步代碼。理解 JavaScript 中 async 的內部原理是非常重要的,學習為什么深入理解所選擇的方法是很重要的。正如編程中的其它東西一樣,每種技術都有其優缺點。

書寫高可用,強壯的異步代碼的 5 條小技巧

1.簡潔:使用 async/await 可以讓你寫更少的代碼。每次書寫 async/await 代碼,你都可以跳過書寫一些不必要的步驟: 比如不用寫 .then 回調,創建匿名函數來處理返回值,命名回調返回值。

// `rp` 是個發起 promise 的工具函數。
rp(‘https://api.example.com/endpoint1").then(function(data) {
 // …
});

對比:

// `rp` 是個發起 promise 的工具函數
var response = await rp(‘https://api.example.com/endpoint1");

2.錯誤處理:Async/await 允許使用日常的 try/catch 代碼結構體來處理同步和異步錯誤。看下和 Promise 是如何寫的:

function loadData() {
    try { // 捕獲同步錯誤.
        getJSON().then(function(response) {
            var parsed = JSON.parse(response);
            console.log(parsed);
        }).catch(function(e) { // 捕獲異步錯誤.
            console.log(e); 
        });
    } catch(e) {
        console.log(e);
    }
}

對比:

async function loadData() {
    try {
        var data = JSON.parse(await getJSON());
        console.log(data);
    } catch(e) {
        console.log(e);
    }
}

3.條件語句:使用 async/await 來書寫條件語句會更加直觀。

function loadData() {
  return getJSON()
    .then(function(response) {
      if (response.needsAnotherRequest) {
        return makeAnotherRequest(response)
          .then(function(anotherResponse) {
            console.log(anotherResponse)
            return anotherResponse
          })
      } else {
        console.log(response)
        return response
      }
    })
}

對比:

async function loadData() {
  var response = await getJSON();
  if (response.needsAnotherRequest) {
    var anotherResponse = await makeAnotherRequest(response);
    console.log(anotherResponse)
    return anotherResponse
  } else {
    console.log(response);
    return response;    
  }
}

4.堆棧楨:和 async/await 不同的是,從鏈式 promise 返回的錯誤堆棧中無法得知發生錯誤的地方。看如下代碼:

function loadData() {
  return callAPromise()
    .then(callback1)
    .then(callback2)
    .then(callback3)
    .then(() => {
      throw new Error("boom");
    })
}
loadData()
  .catch(function(e) {
    console.log(err);

// Error: boom at callAPromise.then.then.then.then (index.js:8:13)
});

對比:

async function loadData() {
  await callAPromise1()
  await callAPromise2()
  await callAPromise3()
  await callAPromise4()
  await callAPromise5()
  throw new Error("boom");
}
loadData()
  .catch(function(e) {
    console.log(err);
    // output
    // Error: boom at loadData (index.js:7:9)
});

5.調試:如果使用 promise,你就會明白調試它們是一場噩夢。例如,如果你在 .then 代碼塊中設置一個斷點并且使用諸如 "stop-over" 的調試快捷鍵,調試器不會移動到下一個 .then 代碼塊,因為調試器只會步進同步代碼。

使用 async/await 你可以就像同步代碼那樣步進到下一個 await 調用。

不僅是程序本身還有庫,書寫異步 JavaScript 代碼都是相當重要的。

參考資源:

https://github.com/getify/You...

https://github.com/getify/You...

http://nikgrozev.com/2017/10/...

本系列持續更新中,Github 地址請查閱這里。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/94863.html

相關文章

  • JavaScript是如何工作:事件循環異步編程崛起+ 5使用 async/await

    摘要:事件循環從回調隊列中獲取并將其推入調用堆棧。執行從調用堆棧中移除從調用堆棧中移除快速回顧值得注意的是,指定了事件循環應該如何工作,這意味著在技術上它屬于引擎的職責范圍,不再僅僅扮演宿主環境的角色。 此篇是 JavaScript是如何工作的第四篇,其它三篇可以看這里: JavaScript是如何工作的:引擎,運行時和調用堆棧的概述! JavaScript是如何工作的:深入V8引擎&編寫...

    Honwhy 評論0 收藏0
  • JavaScript工作原理(四):事件循環異步編程興起以5async/await實踐

    摘要:事件循環從回調隊列中獲取并將其推送到調用堆棧。如何工作請注意,不會自動將您的回調函數放到事件循環隊列中。它設置了一個計時器,當計時器到期時,環境將您的回調函數放入事件循環中,以便將來的某個事件會將其選中并執行它。 我們將通過回顧第一篇文章中單線程編程的缺點,然后在討論如何克服它們來構建令人驚嘆的JavaScript UI。在文章結尾處,我們將分享5個關于如何使用async / awai...

    piglei 評論0 收藏0
  • ES6-7

    摘要:的翻譯文檔由的維護很多人說,阮老師已經有一本關于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...

    mudiyouyou 評論0 收藏0
  • 夯實基礎-JavaScript異步編程

    摘要:調用棧被清空,消息隊列中并無任務,線程停止,事件循環結束。不確定的時間點請求返回,將設定好的回調函數放入消息隊列。調用棧執行完畢執行消息隊列任務。請求并發回調函數執行順序無法確定。 異步編程 JavaScript中異步編程問題可以說是基礎中的重點,也是比較難理解的地方。首先要弄懂的是什么叫異步? 我們的代碼在執行的時候是從上到下按順序執行,一段代碼執行了之后才會執行下一段代碼,這種方式...

    shadowbook 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<