摘要:異步回調(diào)被作為實(shí)參傳入另一函數(shù),并在該外部函數(shù)內(nèi)被調(diào)用,用以來(lái)完成某些任務(wù)的函數(shù),稱為回調(diào)函數(shù)。回調(diào)函數(shù)經(jīng)常被用于繼續(xù)執(zhí)行一個(gè)異步完成后的操作,它們被稱為異步回調(diào)。回調(diào)函數(shù)是事件循環(huán)回頭調(diào)用到程序中的目標(biāo),隊(duì)列處理到這個(gè)項(xiàng)目的時(shí)候會(huì)運(yùn)行它。
唯一比不知道代碼為什么崩潰更可怕的事情是,不知道為什么一開始它是工作的!
在 ECMA 規(guī)范的最近幾次版本里不斷有新成員加入,尤其在處理異步的問(wèn)題上,更是不斷推陳出新。然而,我們?cè)谙硎鼙憷耐瑫r(shí),也應(yīng)該了解異步到底是怎么一回事。
現(xiàn)在與將來(lái)JavaScript 是單線程的,一次只能專注于一件事。如果瀏覽器只靠 JavaScript 引擎線程來(lái)完成所有工作,先不說(shuō)能不能搞定,即使可以,那也會(huì)花費(fèi)很長(zhǎng)時(shí)間。幸好在瀏覽器里 JavaScript 引擎并不孤單,還有 GUI 渲染線程、事件觸發(fā)線程、定時(shí)觸發(fā)器線程、異步http請(qǐng)求線程等其它線程。這些線程之間的協(xié)作才有了我們看到的瀏覽器界面效果(遠(yuǎn)不止這些)。
(盜了一張圖)
一個(gè)程序在執(zhí)行過(guò)程中可能會(huì)有等待用戶輸入、從數(shù)據(jù)庫(kù)或文件系統(tǒng)中請(qǐng)求數(shù)據(jù)、通過(guò)網(wǎng)絡(luò)發(fā)送并等待響應(yīng),或是以固定時(shí)間間隔執(zhí)行重復(fù)任務(wù)(比如動(dòng)畫)等情況。(這些情況,當(dāng)下是無(wú)法得出結(jié)果的,但是一旦有了結(jié)果,我們知道需要去做些什么。)
JavaScript 引擎不是一個(gè)人在戰(zhàn)斗,它把以上的任務(wù)交給其它線程,并計(jì)劃好任務(wù)完成后要做的事,JavaScript 引擎又可以繼續(xù)做自己的事了。從這里可以看出,一個(gè)程序的運(yùn)行包括兩部分,現(xiàn)在運(yùn)行和將來(lái)運(yùn)行。而現(xiàn)在運(yùn)行和將來(lái)運(yùn)行的關(guān)系正是異步編程的核心。
let params = {type:"asynchronous"} let response = ajax(params,"http://someURL.com"); // 異步請(qǐng)求 if (!response) throw "無(wú)數(shù)據(jù)!";
以上代碼肯定會(huì)拋錯(cuò)的,異步請(qǐng)求任務(wù)交出去之后,程序會(huì)繼續(xù)運(yùn)行下去。由于ajax(...) 是異步操作,即使立刻返回結(jié)果,當(dāng)下的 response 也不會(huì)被賦值。一個(gè)是現(xiàn)在,一個(gè)是將來(lái),兩者本就不屬于一個(gè)時(shí)空的。
事件循環(huán)現(xiàn)在和將來(lái)是相對(duì)的,等將來(lái)的時(shí)刻到了,將來(lái)也就成為了現(xiàn)在。
JavaScript 引擎運(yùn)行在宿主環(huán)境中,宿主環(huán)境提供了一種機(jī)制來(lái)處理程序中多個(gè)塊的執(zhí)行,且執(zhí)行每個(gè)塊時(shí)調(diào)用 JavaScript 引擎,這種機(jī)制被稱為事件循環(huán)。即,JavaScript 引擎本身并沒(méi)有時(shí)間的概念,只是一個(gè)按需執(zhí)行 JavaScript 任意代碼片段的環(huán)境。
“事件”(JavaScript 代碼執(zhí)行)調(diào)度總是由包含它的環(huán)境進(jìn)行。
點(diǎn)擊圖片進(jìn)入或點(diǎn)此進(jìn)入:
一個(gè) JavaScript 運(yùn)行時(shí)包含了一個(gè)待處理的消息隊(duì)列。每一個(gè)消息都關(guān)聯(lián)著一個(gè)用以處理這個(gè)消息的函數(shù)。
在事件循環(huán)期間的某個(gè)時(shí)刻,運(yùn)行時(shí)從最先進(jìn)入隊(duì)列的消息開始處理隊(duì)列中的消息。為此,這個(gè)消息會(huì)被移出隊(duì)列,并作為輸入?yún)?shù)調(diào)用與之關(guān)聯(lián)的函數(shù)。
while (queue.waitForMessage()) { queue.processNextMessage(); }
一旦有事件需要進(jìn)行,事件循環(huán)就會(huì)運(yùn)行,直到隊(duì)列清空。事件循環(huán)的每一輪稱為一個(gè) tick。用戶交互,IO 和定時(shí)器會(huì)向事件隊(duì)列中加入事件。
(又盜了一張圖)
任務(wù)隊(duì)列(job queue)建立在事件循環(huán)隊(duì)列之上。(Promise 的異步特性就是基于任務(wù)。)
最好的理解方式,它是掛在事件循環(huán)隊(duì)列的每個(gè)tick之后的一個(gè)隊(duì)列。在事件循環(huán)的每個(gè)tick中,可能出現(xiàn)的異步動(dòng)作不會(huì)導(dǎo)致一個(gè)完整的新事件添加到事件循環(huán)隊(duì)列中,而會(huì)在當(dāng)前 tick 的任務(wù)隊(duì)列末尾添加一個(gè)項(xiàng)目(一個(gè)任務(wù))。
即,由 Call Stack 生成的任務(wù)隊(duì)列會(huì)緊隨其后運(yùn)行。
Promise.resolve().then(function promise1 () { console.log("promise1"); }) setTimeout(function setTimeout1 (){ console.log("setTimeout1"); Promise.resolve().then(function promise2 () { console.log("promise2"); }) }, 0) setTimeout(function setTimeout2 (){ console.log("setTimeout2"); Promise.resolve().then(function promise3 () { console.log("promise3"); setTimeout(function setTimeout3 () { console.log("setTimeout3"); }) Promise.resolve().then(function promise4 () { console.log("promise4"); }) }) }, 0) // promise1 // setTimeout1 // promise2 // setTimeout2 // promise3 // promise4 // setTimeout3異步回調(diào)
被作為實(shí)參傳入另一函數(shù),并在該外部函數(shù)內(nèi)被調(diào)用,用以來(lái)完成某些任務(wù)的函數(shù),稱為回調(diào)函數(shù)。回調(diào)函數(shù)經(jīng)常被用于繼續(xù)執(zhí)行一個(gè)異步完成后的操作,它們被稱為異步回調(diào)。立即執(zhí)行的稱之為同步回調(diào)。
回調(diào)函數(shù)是事件循環(huán)“回頭調(diào)用”到程序中的目標(biāo),隊(duì)列處理到這個(gè)項(xiàng)目的時(shí)候會(huì)運(yùn)行它。
回調(diào)是 JavaScript 語(yǔ)言中最基礎(chǔ)的異步模式。
生活中,我們喜歡和有條理的人打交道,因?yàn)槲覀兊拇竽X習(xí)慣了這種思維模式。然而回調(diào)的使用打破了這種模式,因?yàn)榇a的嵌套使得我們要在不同塊間切換。嵌套越多,邏輯越復(fù)雜,我們也就越難理解和處理代碼,尤其在表達(dá)異步的方式上。
(又盜了一張圖)
除了嵌套的問(wèn)題,異步回調(diào)還存在一些信任問(wèn)題。
回調(diào)性質(zhì)的不確定
調(diào)用回調(diào)方式不確定(沒(méi)調(diào)用,重復(fù)調(diào)用等)
......
針對(duì)第一點(diǎn)的建議是:永遠(yuǎn)異步調(diào)用回調(diào),即使就在事件循環(huán)的下一輪,這樣,所有回調(diào)都是可預(yù)測(cè)的異步調(diào)用了。
在理解這個(gè)建議之前,我們首先了解下控制反轉(zhuǎn),控制反轉(zhuǎn)就是把自己程序一部分的執(zhí)行控制交個(gè)某個(gè)第三方。
let a = 0; // A thirdparty(() => { console.log("a", a); // B }) a++; // C
A 和 C 是現(xiàn)在運(yùn)行的,B 雖然代碼是我們的,但是卻受制于第三方,因?yàn)槲覀儫o(wú)法確定它是現(xiàn)在運(yùn)行還是將來(lái)運(yùn)行的。這里的回調(diào)函數(shù)可能是同步回調(diào)也可能是異步回調(diào)。a 是 0 還是 1,都有可能。
// 同步回調(diào) const thirdparty = cb => { cb(); } // 異步回調(diào) const thirdparty = cb => { setTimeout(() => cb(), 0); }
所以,永遠(yuǎn)異步調(diào)用回調(diào),可預(yù)測(cè)。
function asyncify(fn) { let func = fn; let t = setTimeout(() => { t = null; if (fn) fn(); }, 0); fn = null; return () => { if (t) { fn = func.bind(this, ...arguments); } else { func.apply(this, arguments); } } } let a = 0; thirdparty(asyncify(() => { console.log("a", a); })) a++; // 1
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/101760.html
摘要:而事件循環(huán)是主線程中執(zhí)行棧里的代碼執(zhí)行完畢之后,才開始執(zhí)行的。由此產(chǎn)生的異步事件執(zhí)行會(huì)作為任務(wù)隊(duì)列掛在當(dāng)前循環(huán)的末尾執(zhí)行。在下,觀察者基于監(jiān)聽(tīng)事件的完成情況在下基于多線程創(chuàng)建。 主要問(wèn)題: 1、JS引擎是單線程,如何完成事件循環(huán)的? 2、定時(shí)器函數(shù)為什么計(jì)時(shí)不準(zhǔn)確? 3、回調(diào)與異步,有什么聯(lián)系和不同? 4、ES6的事件循環(huán)有什么變化?Node中呢? 5、異步控制有什么難點(diǎn)?有什么解決方...
摘要:調(diào)用棧被清空,消息隊(duì)列中并無(wú)任務(wù),線程停止,事件循環(huán)結(jié)束。不確定的時(shí)間點(diǎn)請(qǐng)求返回,將設(shè)定好的回調(diào)函數(shù)放入消息隊(duì)列。調(diào)用棧執(zhí)行完畢執(zhí)行消息隊(duì)列任務(wù)。請(qǐng)求并發(fā)回調(diào)函數(shù)執(zhí)行順序無(wú)法確定。 異步編程 JavaScript中異步編程問(wèn)題可以說(shuō)是基礎(chǔ)中的重點(diǎn),也是比較難理解的地方。首先要弄懂的是什么叫異步? 我們的代碼在執(zhí)行的時(shí)候是從上到下按順序執(zhí)行,一段代碼執(zhí)行了之后才會(huì)執(zhí)行下一段代碼,這種方式...
摘要:的翻譯文檔由的維護(hù)很多人說(shuō),阮老師已經(jīng)有一本關(guān)于的書了入門,覺(jué)得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過(guò)程中,顯得越來(lái)越重要。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:只要指定過(guò)這些事件的回調(diào)函數(shù),這些事件發(fā)生時(shí)就會(huì)進(jìn)入任務(wù)隊(duì)列,等待主線程讀取。異步任務(wù)必須指定回調(diào)函數(shù),當(dāng)主線程開始執(zhí)行異步任務(wù),就是執(zhí)行對(duì)應(yīng)的回調(diào)函數(shù)。 javascript語(yǔ)言是一門單線程的語(yǔ)言,不像java語(yǔ)言,類繼承Thread再來(lái)個(gè)thread.start就可以開辟一個(gè)線程。所以,javascript就像一條流水線,僅僅是一條流水線而已,要么加工,要么包裝,不能同時(shí)進(jìn)行多個(gè)任...
摘要:異步那些事一基礎(chǔ)知識(shí)異步那些事二分布式事件異步那些事三異步那些事四異步那些事五異步腳本加載事件概念異步回調(diào)首先了講講中兩個(gè)方法和定義和用法方法用于在指定的毫秒數(shù)后調(diào)用函數(shù)或計(jì)算表達(dá)式。功能在事件循環(huán)的下一次循環(huán)中調(diào)用回調(diào)函數(shù)。 JS異步那些事 一 (基礎(chǔ)知識(shí))JS異步那些事 二 (分布式事件)JS異步那些事 三 (Promise)JS異步那些事 四(HTML 5 Web Workers...
閱讀 3026·2021-11-24 10:32
閱讀 681·2021-11-24 10:19
閱讀 5119·2021-08-11 11:17
閱讀 1463·2019-08-26 13:31
閱讀 1265·2019-08-23 15:15
閱讀 2290·2019-08-23 14:46
閱讀 2273·2019-08-23 14:07
閱讀 1092·2019-08-23 14:03