摘要:事件循環從回調隊列中獲取并將其推送到調用堆棧。如何工作請注意,不會自動將您的回調函數放到事件循環隊列中。它設置了一個計時器,當計時器到期時,環境將您的回調函數放入事件循環中,以便將來的某個事件會將其選中并執行它。
我們將通過回顧第一篇文章中單線程編程的缺點,然后在討論如何克服它們來構建令人驚嘆的JavaScript UI。在文章結尾處,我們將分享5個關于如何使用async / await編寫更簡潔的代碼的技巧。
單線程的局限性第一篇文章中,我們分析了如果在Call Stack中調用耗時長的函數,會產生很多問題。
想象一下,一個復雜圖像轉換算法在瀏覽器中運行。
當Call Stack有函數需要執行的時候,瀏覽器是無法執行其他任何操作的 - 沒錯它被阻塞了。這意味著瀏覽器無法渲染頁面,也不能運行任何其他代碼,它只是卡住了。問題來了 - 您的應用用戶界面不再高效和令人滿意。
在某些情況下,這可能不是至關重要的問題。但是,它可能引起一個更大的問題。一旦您的瀏覽器開始處理Call Stack中的太多任務,它可能會停止響應很長時間。很多瀏覽器會彈出錯誤處理窗口,詢問他們是否應該終止該頁面,這很丑陋,它完全毀了你的用戶體驗:
您可能會把所有JavaScript代碼寫入一個.js文件,但是你的代碼幾乎肯定由幾個塊組成,其中只有一個將立即執行,其余的將在稍后執行。最常見的塊單位是函數。
大多數新的JavaScript的開發者似乎都有這樣的理解,即以后不一定會嚴格地立即發生。換句話說,根據定義,現在無法完成的任務將異步完成,這意味著您不會出現上述阻止行為,因為您可能已經潛意識地預期或期望。
我們來看看下面的例子:
// ajax(..) 是由其它工具庫提供的函數 var response = ajax("https://example.com/api"); console.log(response); // `response` 不會有結果
您可能知道標準的Ajax請求是不會同步完成的,這意味著在代碼執行時,ajax(..)函數還沒有任何值返回以分配給response變量。
一個簡單實現“等待”異步函數返回結果的方法就是callback的函數:
ajax("https://example.com/api", function(response) { console.log(response); // `response` 有值了 });
請注意:您實際上可以創建同步的Ajax請求。但是永遠不要這樣做,如果您發出同步Ajax請求,您的JavaScript應用的用戶界面將被阻塞 - 用戶將無法點擊,或輸入數據,導航或滾動。這將阻止任何用戶交互。沒錯這是一個可怕的做法。
同步ajax請求代碼如下,但請不要這樣做:
// 假設你是用jquery庫 jQuery.ajax({ url: "https://api.example.com/endpoint", success: function(response) { // 你的回調函數 }, async: false // 壞主意 });
Ajax請求只是其中一個例子。你可以讓任何代碼塊異步執行。
這個可以通過setTimeout(回調,毫秒)函數來完成。setTimeout函數的作用是設置一個事件(超時)過一段時間再執行。 讓我們來看看:
function first() { console.log("first"); } function second() { console.log("second"); } function third() { console.log("third"); } first(); setTimeout(second, 1000); // Invoke `second` after 1000ms third();
輸出如下:
first third second解析事件循環
盡管允許異步JavaScript代碼(如我們剛才討論的setTimeout),但直到ES6,JavaScript本身實際上從來沒有內置任何異步的直接概念,但我們將從一個有點奇怪的說法開始。 JavaScript引擎從來沒有做過比在任何特定時刻執行單個程序塊更多的事情。
有關JavaScript引擎如何工作的(特別是Google的V8),請查看本系列第三篇文章。
那么,誰告訴JS引擎來執行你的程序塊?實際上,JS引擎并不是孤立運行的 - 它運行在一個托管環境中,對于大多數開發人員來說,它是Web瀏覽器或Node.js。事實上,現在,JavaScript被嵌入到各種設備中,從機器人到燈泡。每個設備都代表JS Engine的不同類型的托管環境。
所有環境中的共同點是一種稱為事件循環的內置機制,它隨著時間的推移處理程序中多個代碼塊的執行,每次調用JS引擎。
這意味著JS引擎只是JS代碼的按需執行環境。它是調度事件(JS代碼執行)的周圍環境。
例如,當您的JavaScript程序發出Ajax請求,想要從服務器獲取一些數據時,您可以在函數中設置“響應”代碼(“回調”),并且JS引擎會告訴主機環境:
“嘿,我現在暫停執行,但每當你完成這個網絡請求,并且你有一些數據,請執行這個函數。”
然后設置瀏覽器來偵聽來自網絡的響應,當它返回給您時,它將通過將回調函數插入到事件循環中來安排執行回調函數。
我們來看下面的圖表:
您可以在本系列第一篇文章中閱讀關于內存堆和調用堆棧的更多信息。
這些Web API是什么?從本質上講,它們是你無法訪問的線程,你可以對它們進行調用。它們是瀏覽器并發功能的一部分。如果您是Node.js開發人員,那么這些是C++ API。
那么究竟是什么事件循環呢?
事件循環只有一個簡單的工作 - 監視Call Stack(調用堆棧)和Callback Queue(回調隊列)。如果調用堆棧為空,它將從回調隊列中取出第一個事件并將其推送到調用堆棧,該調用堆棧可以有效地運行它。
這種迭代在事件循環中稱為tick。每個事件只是一個函數回調。
console.log("Hi"); setTimeout(function cb1() { console.log("cb1"); }, 5000); console.log("Bye");
讓我們“執行”這段代碼,看看會發生什么:
狀態很清楚。瀏覽器控制臺已清除,并且調用堆棧為空
console.log("Hi")被添加到Call Stack
執行console.log("Hi")
從Call Stack中移除console.log("Hi")
setTimeout(function cb1() { ... })被添加到Call Stack
執行setTimeout(function cb1() { ... }),瀏覽器將創建一個計時器作為Web API的一部分。 它將為您處理倒計時
setTimeout(function cb1() { ... })自身執行結束,然后從Call Stack中移除
console.log("Bye")被添加到Call Stack
執行console.log("Bye")
從Call Stack中移除執行console.log("Bye")
至少5000毫秒后,定時器完成并將cb1回調函數推送到Callback隊列中。
事件循環從回調隊列中獲取cb1并將其推送到調用堆棧。
cb1執行,添加console.log("cb1")到調用堆棧
console.log("cb1")執行
console.log("cb1")從調用堆棧中移除
cb1從調用堆棧中移除
扼要重述:
有趣的是,ES6指定了事件循環應該如何工作,這意味著它在JS引擎的職責范圍內,而不再只是屬于一個托管環境。這種變化的一個主要原因是在ES6中引入了Promises,因為后者需要對事件循環隊列上的調度操作進行直接,細粒度的控制(我們稍后會更詳細地討論它們)。
setTimeout(…)如何工作請注意,setTimeout(...)不會自動將您的回調函數放到事件循環隊列中。它設置了一個計時器,當計時器到期時,環境將您的回調函數放入事件循環中,以便將來的某個tick事件會將其選中并執行它。查看此代碼:
setTimeout(myCallback, 1000);
這并不意味著myCallback將在1000ms之后馬上執行,而是在1000ms之后,myCallback將被添加到隊列中。但是隊列中可能還有其他事件先前已添加 - 您的回調將不得不等待。
有很多關于開始使用JavaScript中的異步代碼的文章和教程,其中提到了setTimeout(callback,0)。 那么,現在你知道Event Loop的作用了,以及setTimeout如何工作:使用0作為第二個參數調用setTimeout只是推遲回調函數執行,直到調用堆棧清空才執行。
看看下面的代碼:
console.log("Hi"); setTimeout(function() { console.log("callback"); }, 0); console.log("Bye");
雖然等待時間設置為0 ms,但瀏覽器控制臺中的結果如下所示:
Hi Bye callbackES6中的Jobs?
ES6中引入了一個名為“Job隊列”的新概念。它是Event Loop隊列頂部的一個層。在處理Promises的異步行為時,您最有可能接觸到它(我們也將討論它們)。
現在我們將簡單介紹這個概念,以便在我們稍后討論Promise的異步行為時,您將了解如何安排和處理這些操作。
想象一下:Job隊列是一個連接到事件循環隊列中每個tick的末尾的隊列。在事件循環的tick期間可能發生的某些異步操作不會導致將全新的事件添加到事件循環隊列中,而是會將一個項(又名Job)添加到當前tick的Job隊列的末尾。
這意味著您可以添加其他功能以便稍后執行,您可以放心,它將在執行任何其他操作之前立即執行。
Job還可以使更多作業添加到同一隊列的末尾。從理論上講,作業“循環”(一個不停地添加其他作業等的作業)可能會無限地旋轉,從而導致需要進入下一個事件循環節點所需的必要資源的程序不足。從概念上講,這與在代碼中僅表示長時間運行或無限循環(如while(true)..)類似。
作業有點像setTimeout(回調,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); });
我們有一個嵌套在一起的三個函數,每個函數代表一個異步過程。
這種代碼通常被稱為“回調地獄”。但“回撥地獄”實際上與嵌套/縮進幾乎沒有任何關系。這是一個比這更深的問題。
首先,我們正在等待“click”事件,然后等待計時器開始工作,然后等待Ajax響應返回,此時它可能會再次重復。
乍一看,這段代碼看起來可以將其異步映射為連續的步驟:
listen("click", function (e) { // .. });
之后:
setTimeout(function(){ // .. }, 500);
最后:
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的值,然后才能在表達式中使用它們。假設我們有一個函數loadX和loadY,分別從服務器加載x和y的值。然后,想象一下,我們有一個函數sum,返回x加y的值。
它可能看起來像這樣(相當丑陋):
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); });
這里有一些非常重要的東西 - 在這個代碼中,我們將x和y作為未來值,并且我們表達了一個操作和(...)(從外部)不關心x或y或者兩者是否都不可用 馬上。
當然,這種基于簡單回調的方法還有很多不足之處。這只是為了解feature values的好處的第一步,而不必擔心它們何時可用。
Promises的值讓我們簡要地看一下我們如何用Promises來表達x + y示例:
function sum(xPromise, yPromise) { // `Promise.all([ .. ])` 接受 promises 數組, // 返回一個新的promise,這個promise會等待所有promise數組完成 return Promise.all([xPromise, yPromise]) // Promise.all被resolved之后, 我們將返回的X和Y相加 .then(function(values){ // `values` 是之前promises數組中每個promise解決之后的信息組成的數組 return values[0] + values[1]; } ); } // `fetchX()` and `fetchY()` 返回promise,promise包含各自的值 // 這個值可能可用也可能不可用 sum(fetchX(), fetchY()) // 我們最終得到一個promise,它返回了兩個數字的和 // 調用 `then(...)` 得到最終值 .then(function(sum){ console.log(sum); });
在這個片段中有兩層Promise。
直接調用fetchX()和fetchY(),并將它們返回的值(promise!)傳遞給sum(...)。這些承諾所代表的基礎價值可能現在已經準備就緒,但是每個承諾都將其行為規范化為無論如何都是相同的。我們以時間無關的方式推測x和y值。他們是未來的價值觀,期限。
第二層是sum(...)創建的承諾
(通過Promise.all([...]))和返回,我們通過調用然后等待(...)。總和(...)操作完成后,我們的總和未來值已準備好,我們可以將其打印出來。我們隱藏了等待sum(...)中x和y未來值的邏輯。
注意:Inside sum(...)中,Promise.all([...])調用創建一個承諾(等待promiseX并promiseY解析)。然后(...)的鏈接調用創建了另一個承諾,即返回
值[0] +值[1]行立即解決(與加法的結果)。因此,我們連接sum(...)調用結束時的then(...)調用 - 在片段結尾處 - 實際上是在返回的第二個promise上運行,而不是由Promise創建的第一個promise。全部([...])。另外,雖然我們并沒有把時間的尾端連接起來(...),但是如果我們選擇觀察/使用它,它也創造了另一個承諾。本章后面將詳細解釋這個Promise鏈接的東西。
有了Promises,那么(...)調用實際上可以采用兩個函數,第一個用于履行(如前所示),第二個用于拒絕:
sum(fetchX(), fetchY()) .then( // fullfillment handler function(sum) { console.log( sum ); }, // rejection handler function(err) { console.error( err ); // bummer! } );
如果在獲取x或y時出現問題,或者在添加期間某種方式失敗了,那么sum(...)返回的promise將被拒絕,并且傳遞給then(...)的第二個回調錯誤處理程序將收到拒絕 來自諾言的價值。
由于Promises封裝了時間依賴狀態 - 等待基礎價值的實現或拒絕 - 從外部看,Promise本身是時間無關的,因此Promises可以以可預測的方式組合(組合),而不管時間或結果如何 下。
而且,一旦一個承諾解決了,它就會永遠保持這種狀態 - 它在那個時候成為一個不變的價值 - 然后可以根據需要多次觀察。
確實可以鏈接承諾是非常有用的:
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); }) // ...
呼叫延遲(2000)創建了一個將在2000ms完成的承諾,然后我們從第一個(...)履行回調中返回,這導致第二個(...)的承諾等待2000ms的承諾。
注意:因為Promise一旦解決就是外部不可變的,現在可以安全地將該值傳遞給任何一方,并知道它不能被意外或惡意修改。 關于觀察解決諾言的多方,這一點尤其如此。 一方不可能影響另一方遵守Promise解決方案的能力。 不變性可能聽起來像是一個學術話題,但它實際上是Promise設計的最基本和最重要的方面之一,不應該隨便傳遞。
使用還是不使用Promise關于Promises的一個重要細節是確切地知道某個值是否是實際的Promises。 換句話說,這是一種會表現得像一個Promise?
我們知道Promise是由new Promise(...)語法構造的,您可能認為Promise的instanceof將是一個有效的檢查。好吧,不是。
主要是因為您可以從另一個瀏覽器窗口(例如iframe)接收Promise值,該窗口具有與當前窗口或框架中的承諾不同的Promise,并且該檢查無法識別Promise實例。
此外,庫或框架可能會選擇出售自己的Promises,而不是使用原生ES6的Promise實施來實現。 事實上,你可能會在早期的瀏覽器中使用Promises和Promise來實現Promise。
異常如果在創建Promise或觀察其解決方案的任何時候發生JavaScript異常錯誤(例如TypeError或ReferenceError),該異常將被捕獲,并且它將強制有問題的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被實現時,在observation期間(在一個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,這就是那個將被TypeError異常拒絕的promise
處理未捕獲的異常還有其他的方法,很多人會說更好。
一個常見的建議是Promise應該使用done(...),它們基本上將Promise鏈標記為“已完成”。done(...)不會創建并返回Promise,所以回調函數傳遞給done(..)顯然沒有連接到向不存在的鏈式承諾報告問題。
它的處理方式與您在未捕獲的錯誤情況中通常所期待的一樣:done(..)里面的異常或錯誤,將作為全局未捕獲錯誤引發(基本上在開發人員控制臺中):
var p = Promise.resolve(374); p.then(function fulfilled(msg){ // 數字是不會有字符串的處理函數 // 所以會拋出異常 console.log(msg.toLowerCase()); }) .done(null, function() { // 如果異常在這里發生,它會全局拋出 });ES8(ES2017)async/await
JavaScript ES8(ES2017)引入了async/await,這使得使用Promises的工作更容易。我們將簡要介紹async/await提供的可能性以及如何利用它們來編寫異步代碼。
那么,讓我們看看async/await如何工作。
您可以使用async關鍵字聲明定義一個異步函數。這樣的函數返回一個AsyncFunction對象。 AsyncFunction對象表示執行該函數中包含的代碼的異步函數。
當一個異步函數被調用時,它返回一個Promise。當異步函數返回一個值時,這不是一個Promise,Promise將會自動創建,并且會使用函數返回的值來解析。當異步函數拋出異常時,Promise將被拋出的值拒絕。
異步函數可以包含await表達式,暫停執行該函數并等待傳遞的Promise的解析,然后恢復異步函數的執行并返回解析后的值。
您可以將JavaScript中的Promise等同于Java的Future或C#的Task。
async/await的目的是為了簡化使用promises。
我們來看看下面的例子:
// 標準的javascript函數 function getNumber1() { return Promise.resolve("374"); } // 功能和getNumber相同 async function getNumber2() { return 374; }
同樣,拋出異常的函數等價于返回已被reject的promise的函數:
function f1() { return Promise.reject("Some error"); } async function f2() { throw "Some error"; }
await關鍵字只能用于異步功能,并允許您同步等待Promise。 如果我們在異步函數之外使用promise,我們仍然必須使用回調函數:
async function loadData() { // `rp` is a request-promise function. var promise1 = rp("https://api.example.com/endpoint1"); var promise2 = rp("https://api.example.com/endpoint2"); // Currently, both requests are fired, concurrently and // now we"ll have to wait for them to finish var response1 = await promise1; var response2 = await promise2; return response1 + " " + response2; } // Since, we"re not in an `async function` anymore // we have to use `then`. loadData().then(() => console.log("Done"));
還可以使用“異步函數表達式”來定義異步函數。 異步函數表達式與異步函數語句非常相似,語法幾乎相同。異步函數表達式和異步函數語句之間的主要區別在于函數名稱,在異步函數表達式中可以省略這些名稱以創建匿名函數。異步函數表達式可以用作IIFE(立即調用的函數表達式),只要定義它就立即運行。
它看起來像這樣:
var loadData = async function() { // `rp` is a request-promise function. var promise1 = rp("https://api.example.com/endpoint1"); var promise2 = rp("https://api.example.com/endpoint2"); // Currently, both requests are fired, concurrently and // now we"ll have to wait for them to finish var response1 = await promise1; var response2 = await promise2; return response1 + " " + response2; }
更重要的是,所有主流瀏覽器都支持async/await:
工作一天結束時,重要的是不要盲目選擇“最新”方法編寫異步代碼。理解異步JavaScript的內部特性至關重要,并深入了解所選方法的內部原理。與編程中的其他所有方法一樣,每種方法都有優點和缺點。
編寫高度可維護,強壯的異步代碼的5個技巧代碼整潔:使用async/await可以減少你的代碼體積,因為它可以略過一些不必要的步驟:.then鏈,處理結果的匿名函數和回調函數中定義結果變量
// `rp` is a request-promise function. rp(‘https://api.example.com/endpoint1").then(function(data) { // … });
使用async/await之后:
// `rp` is a request-promise function. var response = await rp(‘https://api.example.com/endpoint1");
錯誤處理:
async/await使相同的代碼結構來處理同步或異步的錯誤(或異常)稱為可能,比如熟悉的try/catch語句,下面的例子使用Promises:
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/await之后:
async function loadData() { try { var data = JSON.parse(await getJSON()); console.log(data); } catch(e) { console.log(e); } }
使用條件:使用條件式代碼結合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/await之后:
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; } }
堆棧幀:
使用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/await之后:
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) });
調試:如果你使用過promises,你知道調試它們是一場噩夢。例如,如果您在.then塊內設置斷點并使用調試快捷方式(如“step over”),則調試器將不會移動到以下位置,因為它僅通過同步代碼“執行”。
通過異步/等待,您可以完全按照正常的同步功能一步一步地調試。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107698.html
摘要:函數會在之后的某個時刻觸發事件定時器。事件循環中的這樣一次遍歷被稱為一個。執行完畢并出棧。當定時器過期,宿主環境會把回調函數添加至事件循環隊列中,然后,在未來的某個取出并執行該事件。 原文請查閱這里,略有改動。 本系列持續更新中,Github 地址請查閱這里。 這是 JavaScript 工作原理的第四章。 現在,我們將會通過回顧單線程環境下編程的弊端及如何克服這些困難以創建令人驚嘆...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。寫一個符合規范并可配合使用的寫一個符合規范并可配合使用的理解的工作原理采用回調函數來處理異步編程。 JavaScript怎么使用循環代替(異步)遞歸 問題描述 在開發過程中,遇到一個需求:在系統初始化時通過http獲取一個第三方服務器端的列表,第三方服務器提供了一個接口,可通過...
摘要:的翻譯文檔由的維護很多人說,阮老師已經有一本關于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:調用棧被清空,消息隊列中并無任務,線程停止,事件循環結束。不確定的時間點請求返回,將設定好的回調函數放入消息隊列。調用棧執行完畢執行消息隊列任務。請求并發回調函數執行順序無法確定。 異步編程 JavaScript中異步編程問題可以說是基礎中的重點,也是比較難理解的地方。首先要弄懂的是什么叫異步? 我們的代碼在執行的時候是從上到下按順序執行,一段代碼執行了之后才會執行下一段代碼,這種方式...
閱讀 869·2021-09-02 09:55
閱讀 1495·2019-12-27 12:02
閱讀 1683·2019-08-30 14:24
閱讀 1137·2019-08-30 14:18
閱讀 2749·2019-08-29 13:57
閱讀 2193·2019-08-26 11:51
閱讀 1363·2019-08-26 10:37
閱讀 762·2019-08-23 16:09