JS常用的幾種非同步流程控制
JavaScript引擎是基於單執行緒 (Single-threaded) 事件迴圈的概念構建的,同一時刻只允許一個程式碼塊在執行,所以需要跟蹤即將執行的程式碼,那些程式碼被放在一個任務佇列 (job queue) 中,每當一段程式碼準備執行時,都會被新增到任務佇列中。每當JavaScript引擎中的一段程式碼結束執行,時間迴圈 (event loop) 會執行佇列中的下一個任務,它是 JavaScript 引擎中的一段程式,負責監控程式碼執行並管理任務佇列。
請記住,佇列中的任務會從第一個一直執行到最後一個。
事件模型
JavaScript最基礎的非同步程式設計形式(比如點選事件、鍵盤事件)
直到事件觸發時才執行處理程式
回撥模式
回撥模式與事件模型類似,非同步程式碼都會在未來的某個時間點執行,而這的區別是回撥模式中被呼叫的函式是作為引數傳入的。
Node.js讀取磁碟上的檔案:
readFile('example.txt', function(err, contents) { if(err) { throw err } console.log(contents) }) console.log('Hi!')
呼叫readFile函式後,console.log('Hi!')語句會立即執行,當readFile結束執行的時候,會向任務佇列的末尾新增一個新任務,該任務包含回撥函式及相應的引數。
雖然這個模式執行效果很不錯,但是如果嵌套了太多的回撥函式,就會陷入回撥地獄。
當需要跟蹤多個回撥函式的時候,回撥函式的侷限性就體現出來了,Promise非常好的改進了這些情況。
Promise
Promise 物件是一個代理物件(代理一個值),被代理的值在Promise物件建立時可能是未知的。它允許你為非同步操作的成功和失敗分別繫結相應的處理方法(handlers)。 這讓非同步方法可以像同步方法那樣返回值,但並不是立即返回最終執行結果,而是一個能代表未來出現的結果的promise物件
一個 Promise有以下幾種狀態:
pending: 初始狀態,既不是成功,也不是失敗狀態。 fulfilled: 意味著操作成功完成。 rejected: 意味著操作失敗。
pending 狀態的 Promise 物件可能觸發fulfilled 狀態並傳遞一個值給相應的狀態處理方法,也可能觸發失敗狀態(rejected)並傳遞失敗資訊。當其中任一種情況出現時,Promise 物件的 then 方法繫結的處理方法(handlers )就會被呼叫(then方法包含兩個引數:onfulfilled 和 onrejected,它們都是 Function 型別。當Promise狀態為fulfilled時,呼叫 then 的 onfulfilled 方法,當Promise狀態為rejected時,呼叫 then 的 onrejected 方法, 所以在非同步操作的完成和繫結處理方法之間不存在競爭)。
因為 Promise.prototype.then 和Promise.prototype.catch 方法返回promise 物件, 所以它們可以被鏈式呼叫。
var promise1 = new Promise(function(resolve, reject) { setTimeout(function() { resolve('foo'); }, 300); }); promise1.then(function(value) { console.log(value); // expected output: "foo" }); console.log(promise1); // expected output: [object Promise]
async/await
當呼叫一個 async 函式時,會返回一個 Promise 物件。當這個 async 函式返回一個值時,Promise 的 resolve 方法會負責傳遞這個值;當 async 函式丟擲異常時,Promise 的 reject 方法也會傳遞這個異常值。
async 函式中可能會有 await 表示式,這會使 async 函式暫停執行,等待 Promise的結果出來,然後恢復async函式的執行並返回解析值(resolved)。
注意, await 關鍵字僅僅在 async function中有效。如果在 async function函式體外使用 await ,你只會得到一個語法錯誤(SyntaxError)。
async function testAsync() { return "Hello"; } const testResult = testAsync(); console.log(testResult); //Promise { 'Hello' } testAsync().then(v => { console.log(v);// 輸出 Hello }); //使用await async function testAsnync1() { let result = await testAsync(); console.log(result); // Hello }
參考
《深入理解ES6》