摘要:一異步編程原理顯然,上面這種方式和銀行取號(hào)等待有些類(lèi)似,只不過(guò)銀行取號(hào)我們并不知道上一個(gè)人需要多久才會(huì)完成。下面來(lái)探討下中的異步編程原理。
眾所周知,JavaScript 的執(zhí)行環(huán)境是單線程的,所謂的單線程就是一次只能完成一個(gè)任務(wù),其任務(wù)的調(diào)度方式就是排隊(duì),這就和火車(chē)站洗手間門(mén)口的等待一樣,前面的那個(gè)人沒(méi)有搞定,你就只能站在后面排隊(duì)等著。在事件隊(duì)列中加一個(gè)延時(shí),這樣的問(wèn)題便可以得到緩解。
A: 嘿,哥們兒,快點(diǎn)! B: 我要三分鐘,你先等著,完了叫你~ A: 好的,記得叫我啊~ 你(C)也等著吧,完了叫你~ C: 嗯!
告訴后面排隊(duì)的人一個(gè)準(zhǔn)確的時(shí)間,這樣后面的人就可以利用這段時(shí)間去干點(diǎn)別的事情,而不是所有的人都排在隊(duì)列后抱怨。我寫(xiě)了一段程序來(lái)解決這個(gè)問(wèn)題:
/** * @author Barret Lee * @email barret.china@gmail.com * @description 事件隊(duì)列管理,含延時(shí) */ var Q = { // 保存隊(duì)列信息 a: [], // 添加到隊(duì)列 queue q: function(d){ // 添加到隊(duì)列如果不是函數(shù)或者數(shù)字則不處理 if(!/function|number/.test(typeof d)) return; Q.a.push(d); // 返回對(duì)自身的引用 return Q; }, // 執(zhí)行隊(duì)列 dequeue d: function(){ var s = Q.a.shift(); // 如果已經(jīng)到了隊(duì)列盡頭則返回 if(!s) return; // 如果是函數(shù),直接執(zhí)行,然后繼續(xù) dequeue if(typeof s === "function") { s(), Q.d(); return; } // 如果是數(shù)字,該數(shù)字作為延遲時(shí)間,延遲 dequeue setTimeout(function(){ Q.d(); }, s); } };
這段程序加了很多注釋?zhuān)嘈庞?JS 基礎(chǔ)的童鞋都能夠看懂,利用上面這段代碼測(cè)試下:
// 進(jìn)程記錄函數(shù)
function record(s){
var div = document.createElement("div");
div.innerHTML = s;
console.log(s);
document.body.appendChild(div);
}
Q
.q(function(){
record("0 3s 之后搞定,0 把 1 叫進(jìn)來(lái)");
})
.q(3000) // 延時(shí) 3s
.q(function(){
record("1 2s 之后搞定,1 把 2 叫進(jìn)來(lái)");
})
.q(2000) // 延時(shí) 2s
.q(function(){
record("2 后面沒(méi)人了,OK,廁所關(guān)門(mén)~");
})
.d(); // 執(zhí)行隊(duì)列
可以戳戳這個(gè) DEMO。
本文地址:http://barretlee.github.io/javascript-asynchronous-programming,轉(zhuǎn)載請(qǐng)注明出處。
一、Javascript 異步編程原理顯然,上面這種方式和銀行取號(hào)等待有些類(lèi)似,只不過(guò)銀行取號(hào)我們并不知道上一個(gè)人需要多久才會(huì)完成。這是一種非阻塞的方式處理問(wèn)題。下面來(lái)探討下 JavaScript 中的異步編程原理。
1. setTimeout 函數(shù)的弊端延時(shí)處理當(dāng)然少不了 setTimeout 這個(gè)神器,很多人對(duì) setTimeout 函數(shù)的理解就是:延時(shí)為 n 的話,函數(shù)會(huì)在 n 毫秒之后執(zhí)行。事實(shí)上并非如此,這里存在三個(gè)問(wèn)題,一個(gè)是 setTimeout 函數(shù)的及時(shí)性問(wèn)題,可以測(cè)試下面這串代碼:
/var d = new Date, count = 0, f, timer; timer = setInterval(f = function (){ if(new Date - d > 1000) clearInterval(timer), console.log(count); count++; }, 0);
可以看出 1s 中運(yùn)行的次數(shù)大概在 200次 左右,有人會(huì)說(shuō)那是因?yàn)?new Date 和 函數(shù)作用域的轉(zhuǎn)換消耗了時(shí)間,其實(shí)不然,你可以再試試這段代碼:
var d = new Date, count = 0; while(true) { if(new Date - d > 1000) { console.log(count); break; } count++; }
我這里顯示的是 351813,也就是說(shuō) count 累加了 35W+ 次,這說(shuō)明了什么呢?setInterval 和 setTimeout 函數(shù)運(yùn)轉(zhuǎn)的最短周期是 5ms 左右,這個(gè)數(shù)值在 HTML規(guī)范 中也是有提到的:
5. Let timeout be the second method argument, or zero if the argument was omitted. 如果 timeout 參數(shù)沒(méi)有寫(xiě),默認(rèn)為 0 7. If nesting level is greater than 5, and timeout is less than 4, then increase timeout to 4. 如果嵌套的層次大于 5 ,并且 timeout 設(shè)置的數(shù)值小于 4 則直接取 4.
為了讓函數(shù)可以更快速的相應(yīng),部分瀏覽器提供了更加高級(jí)的接口(當(dāng) timeout 為 0 的時(shí)候,可以使用下面的方式替代,速度更快):
requestAnimationFrame 它允許 JavaScript 以 60+幀/s 的速度處理動(dòng)畫(huà),他的運(yùn)行時(shí)間間隔比 setTimeout 是要短很多的。
process.nextTick 這個(gè)是 NodeJS 中的一個(gè)函數(shù),利用他可以幾乎達(dá)到上面看到的 while 循環(huán)的效率
ajax 或者 插入節(jié)點(diǎn) 的 readState 變化
MutationObserver
setImmediate
...
這些東西下次有空再細(xì)談。之前研究司徒正美的 avalon 源碼的時(shí)候,看到了相關(guān)的內(nèi)容,有興趣的可以看看:
//視瀏覽器情況采用最快的異步回調(diào) var BrowserMutationObserver = window.MutationObserver || window.WebKitMutationObserver if (BrowserMutationObserver) { //chrome18+, safari6+, firefox14+,ie11+,opera15 avalon.nextTick = function(callback) { //2-3ms var input = DOC.createElement("input") var observer = new BrowserMutationObserver(function(mutations) { mutations.forEach(function() { callback() }) }) observer.observe(input, { attributes: true }) input.setAttribute("value", Math.random()) } } else if (window.VBArray) { //IE下這個(gè)通常只要1ms,而且沒(méi)有副作用,不會(huì)發(fā)現(xiàn)請(qǐng)求, //setImmediate如果只執(zhí)行一次,與setTimeout一樣要140ms上下 avalon.nextTick = function(callback) { var node = DOC.createElement("script") node.onreadystatechange = function() { callback() //在interactive階段就觸發(fā) node.onreadystatechange = null root.removeChild(node) node = null } root.appendChild(node) } } else { avalon.nextTick = function(callback) { setTimeout(callback, 0) } }
上面說(shuō)了一堆,目的是想說(shuō)明, setTimeout 是存在一定時(shí)間間隔的,并不是設(shè)定 n 毫秒執(zhí)行,他就是 n 毫秒執(zhí)行,可能會(huì)有一點(diǎn)時(shí)間的延遲(2ms左右)。然后說(shuō)說(shuō)他的第二個(gè)缺點(diǎn),先看代碼:
var d = new Date; setTimeout(function(){ console.log("show me after 1s, but you konw:" + (new Date - d)); }, 1000); while(1) if(new Date - d > 2000) break;
我們期望 console 在 1s 之后出結(jié)果,可事實(shí)上他卻是在 2075ms 之后運(yùn)行的,這就是 JavaScript 單線程給我們帶來(lái)的煩惱,while循環(huán)阻塞了 setTimeout 函數(shù)的執(zhí)行。接著是他的第三個(gè)毛病,try..catch捕捉不到他的錯(cuò)誤:
try{ setTimeout(function(){ throw new Error("我不希望這個(gè)錯(cuò)誤出現(xiàn)!") }, 1000); } catch(e){ console.log(e.message); }
可以說(shuō) setTimeout 是異步編程不可缺少的角色,但是它本身就存在這么多的問(wèn)題,這就要求我們用更加恰當(dāng)?shù)姆绞饺ヒ?guī)避!
2. 什么樣的函數(shù)為異步的異步的概念和非阻塞是是息息相關(guān)的,我們通過(guò) ajax 請(qǐng)求數(shù)據(jù)的時(shí)候,一般采用的是異步的方式:
var xhr = new XMLHttpRequest(); xhr.open("GET", "/", true); xhr.send(); xhr.onreadystatechange = function(){ console.log(xhr.status); }
在 xhr.open 中我們把第三個(gè)參數(shù)設(shè)置為 true ,也就是異步加載,當(dāng) state 發(fā)生改變的時(shí)候,xhr 立即響應(yīng),觸發(fā)相關(guān)的函數(shù)。有人想過(guò)用這樣的方式來(lái)處理:
while(1) { if(xhr.status === "complete") { // dosomething(); break; } }
而事實(shí)上,這里的判斷已經(jīng)陷入了死循環(huán),即便是 xhr 的 status 已經(jīng)發(fā)生了改變,這個(gè)死循環(huán)也跳不出來(lái),那么這里的異步是基于事件的。
某個(gè)函數(shù)會(huì)導(dǎo)致將來(lái)再運(yùn)行的另一個(gè)函數(shù),后者取自于事件隊(duì)列(若后面這個(gè)函數(shù)是作為參數(shù)傳遞給前者的,則稱(chēng)其為回調(diào)函數(shù),簡(jiǎn)稱(chēng)為回調(diào))。—— 摘自《Async Javascript》
由于 JavaScript 的單線程特點(diǎn),他沒(méi)有提供一種機(jī)制以阻止函數(shù)在其異步操作結(jié)束之前返回,事實(shí)上,除非函數(shù)返回,否則不會(huì)觸發(fā)任何異步事件。
3. 常見(jiàn)的異步模型1) 最常見(jiàn)的一種方式是,高階函數(shù)(泛函數(shù))
step1(function(res1){ step2(function(res2){ step3(function(res3){ //... }); }); });
解耦程度特別低,如果送入的參數(shù)太多會(huì)顯得很亂!這是最常見(jiàn)的一種方式,把函數(shù)作為參數(shù)送入,然后回調(diào)。
2) 事件監(jiān)聽(tīng)
f.on("evt", g); function f(){ setTimeout(function(){ f.trigger("evt"); }) }
JS 和 瀏覽器提供的原生方法基本都是基于事件觸發(fā)機(jī)制的,耦合度很低,不過(guò)事件不能得到流程控制。
3) 發(fā)布/訂閱( Pub/Sub )
E.subscribe("evt", g); function f(){ setTimeout(function () { // f的任務(wù)代碼 E.publish("evt"); }, 1000); }
把事件全部交給 E 這個(gè)控制器管理,可以完全掌握事件被訂閱的次數(shù),以及訂閱者的信息,管理起來(lái)特別方便。
4) Promise 對(duì)象(deferred 對(duì)象)
關(guān)于這里的內(nèi)容可以看看 屈屈 寫(xiě)的文章,說(shuō)的比較詳細(xì)。
Promise/A+ 規(guī)范是對(duì) Promise/A 規(guī)范的補(bǔ)充和修改,他出現(xiàn)的目的是為了統(tǒng)一異步編程中的接口,JS中的異步編程是十分普遍的事情,也出現(xiàn)了很多的異步庫(kù),如果不統(tǒng)一接口,對(duì)開(kāi)發(fā)者來(lái)說(shuō)也是一件十分痛苦的事情。
在Promises/A規(guī)范中,每個(gè)任務(wù)都有三種狀態(tài):默認(rèn)(pending)、完成(fulfilled)、失敗(rejected)。
默認(rèn)狀態(tài)可以單向轉(zhuǎn)移到完成狀態(tài),這個(gè)過(guò)程叫resolve,對(duì)應(yīng)的方法是deferred.resolve(promiseOrValue);
默認(rèn)狀態(tài)還可以單向轉(zhuǎn)移到失敗狀態(tài),這個(gè)過(guò)程叫reject,對(duì)應(yīng)的方法是deferred.reject(reason);
默認(rèn)狀態(tài)時(shí),還可以通過(guò)deferred.notify(update)來(lái)宣告任務(wù)執(zhí)行信息,如執(zhí)行進(jìn)度;
狀態(tài)的轉(zhuǎn)移是一次性的,一旦任務(wù)由初始的pending轉(zhuǎn)為其他狀態(tài),就會(huì)進(jìn)入到下一個(gè)任務(wù)的執(zhí)行過(guò)程中。
二、異步函數(shù)中的錯(cuò)誤處理前面已經(jīng)提到了 setTimeout 函數(shù)的一些問(wèn)題,JS 中的 try..catch 機(jī)制并不能拿到 setTimeout 函數(shù)中出現(xiàn)的錯(cuò)誤,一個(gè) throw error 的影響范圍有多大呢?我做了一個(gè)測(cè)試:
從上面的測(cè)試我們可以看出,throw new Error 的作用范圍就是阻斷一個(gè) script 標(biāo)簽內(nèi)的程序運(yùn)行,但是不會(huì)影響下面的 script。這個(gè)測(cè)試沒(méi)什么作用,只是想告訴大家不要擔(dān)心一個(gè) Error 會(huì)影響全局的函數(shù)執(zhí)行。所以把代碼分為兩段,一段可能出錯(cuò)的,一段確保不會(huì)出錯(cuò)的,這樣不至于讓全局代碼都死掉,當(dāng)然這樣的處理方式是不可取的。
慶幸的是 window 全局對(duì)象上有一個(gè)便利的函數(shù),window.error,我們可以利用他捕捉到所有的錯(cuò)誤,并作出相應(yīng)的處理,比如:
window.onerror = function(msg, url, line){ console.log(msg, url, line); // 必須返回 true,否則 Error 還是會(huì)觸發(fā)阻塞程序 return true; } setTimeout(function(){ throw new Error("error"); // console: //Uncaught Error: error path/to/ie6bug.html 99 }, 50);
我們可以對(duì)錯(cuò)誤進(jìn)行封裝處理:
window.onerror = function(msg, url, line){ // 截?cái)?"Uncaught Error: error",獲取錯(cuò)誤類(lèi)型 var type = msg.slice(16); switch(type){ case "TooLarge": console.log("The number is too large"); case "TooSmall": console.log("The number is too Small"); case "TooUgly": console.log("That"s Barret Lee~"); // 如果不是我們預(yù)定義的錯(cuò)誤類(lèi)型,則反饋給后臺(tái)監(jiān)控 default: $ && $.post && $.post({ "msg": msg, "url": url, "line": line }) } // 記得這里要返回 true,否則錯(cuò)誤阻斷程序。 return true; } setTimeout(function(){ if( something ) throw new Error("TooUgly"); // console: //That"s Barret Lee~ }, 50);
很顯然,報(bào)錯(cuò)已經(jīng)不可怕了,利用 window 提供的 onerror 函數(shù)可以很方便地處理錯(cuò)誤并作出及時(shí)的反應(yīng),如果出現(xiàn)了不可知的錯(cuò)誤,可以把信息 post 到后臺(tái),這也算是一個(gè)十分不錯(cuò)的監(jiān)控方式。
不過(guò)這樣的處理存在一個(gè)問(wèn)題,所有的錯(cuò)誤我們都給屏蔽了,但有些錯(cuò)誤本應(yīng)該阻斷所有程序的運(yùn)行的。比如我們通過(guò) ajax 獲取數(shù)據(jù)中出了錯(cuò)誤,程序誤以為已經(jīng)拿到了數(shù)據(jù),本應(yīng)該停下工作報(bào)出這個(gè)致命的錯(cuò)誤,但是這個(gè)錯(cuò)誤被 window.onerror 給截獲了,從而進(jìn)行了錯(cuò)誤的處理。
window.onerror 算是一種特別暴力的容錯(cuò)手段,try..catch 也是如此,他們底層的實(shí)現(xiàn)就是利用 C/C++ 中的 goto 語(yǔ)句實(shí)現(xiàn),一旦發(fā)現(xiàn)錯(cuò)誤,不管目前的堆棧有多深,不管代碼運(yùn)行到了何處,直接跑到 頂層 或者 try..catch 捕獲的那一層,這種一腳踢開(kāi)錯(cuò)誤的處理方式并不是很好,我覺(jué)得。
三、JavaScript 多線程技術(shù)介紹開(kāi)始說(shuō)了異步編程和非阻塞這個(gè)概念密切相關(guān),而 JavaScript 中的 Worker 對(duì)象可以創(chuàng)建一個(gè)獨(dú)立線程來(lái)處理數(shù)據(jù),很自然的處理了阻塞問(wèn)題。我們可以把繁重的計(jì)算任務(wù)交給 Worker 去倒騰,等他處理完了再把數(shù)據(jù) Post 過(guò)來(lái)。
var worker = new Worker("./outer.js"); worker.addEventListener("message", function(e){ console.log(e.message); }); worker.postMessage("data one"); worker.postMessage("data two"); // outer.js self.addEventListener("message", function(e){ self.postMessage(e.message); });
上面是一個(gè)簡(jiǎn)單的例子,如果我們創(chuàng)建了多個(gè) Worker,在監(jiān)聽(tīng) onmessage 事件的時(shí)候還要判斷下 e.target 的值從而得知數(shù)據(jù)源,當(dāng)然,我們也可以把數(shù)據(jù)源封裝在 e.message 中。
Worker 是一個(gè)有用的工具,我可以可以在 Worker 中使用 setTimeout,setInterval等函數(shù),也可以拿到 navigator 的相關(guān)信息,最重要的是他可以創(chuàng)建 ajax 對(duì)象和 WebSocket 對(duì)象,也就是說(shuō)他可以直接向服務(wù)器請(qǐng)求數(shù)據(jù)。不過(guò)他不能訪問(wèn) DOM 的信息,更不能直接處理 DOM,這個(gè)其實(shí)很好理解,主線程和 Worker 是兩個(gè)獨(dú)立的線程,如果兩者都可以修改 DOM,那豈不是得設(shè)置一個(gè)麻煩的互斥變量?!還有一個(gè)值得注意的點(diǎn)是,在 Worker 中我們可以使用 importScript 函數(shù)直接加載腳本,不過(guò)這個(gè)函數(shù)是同步的,也就是說(shuō)他會(huì)凍結(jié) Worker 線程,直到 Script 加載完畢。
importScript("a.js", "b.js", "c.js");
他可以添加多個(gè)參數(shù),加載的順序就是 參數(shù)的順序。一般會(huì)使用 Worker 做哪些事情呢?
數(shù)據(jù)的計(jì)算和加密 如計(jì)算斐波拉契函數(shù)的值,特別費(fèi)時(shí);再比如文件的 MD5 值比對(duì),一個(gè)大文件的 MD5 值計(jì)算也是很費(fèi)時(shí)的。
音、視頻流的編解碼工作,這些工作搞微信的技術(shù)人員應(yīng)該沒(méi)有少做。有興趣的童鞋可以看看這個(gè)技術(shù)分享,是杭州的 hehe123 搞的一個(gè)WebRTC 分享,內(nèi)容還不錯(cuò)。
等等,你覺(jué)得費(fèi)時(shí)間的事情都可以交給他做
然后要說(shuō)的是 SharedWorker,這是 web 通信領(lǐng)域未來(lái)的一個(gè)趨勢(shì),有些人覺(jué)得 WebSocket 已經(jīng)十分不錯(cuò)了,但是一些基于 WebSocket 的架構(gòu),服務(wù)器要為每一個(gè)頁(yè)面維護(hù)一個(gè) WebSocket 代碼,而 SharedWorker 十分給力,他是多頁(yè)面通用的。
// outer.js var pool = []; onconnect = function(e) { // 把連接的頁(yè)面放入連接池 pool.push(e.ports[0]); // 收到信息立即廣播 e.ports[0].onmessage = function(e){ for(var i = 0;i < pool.length; i++) // 廣播信息 pool[i].postMessage(e.data); }; };
簡(jiǎn)單理解 SharedWorker,就是把運(yùn)行的一個(gè)線程作為 web后臺(tái)程序,完全不需要后臺(tái)腳本參與,這個(gè)對(duì) web通訊,尤其是游戲開(kāi)發(fā)者,覺(jué)得是一個(gè)福音!
四、ECMAScript 6 中 Generator 對(duì)象搞定異步異步兩種常見(jiàn)方式是 事件監(jiān)聽(tīng) 以及 函數(shù)回調(diào)。前者沒(méi)什么好說(shuō)的,事件機(jī)制是 JS 的核心,而函數(shù)回調(diào)這塊,過(guò)于深入的嵌套簡(jiǎn)直就是一個(gè)地獄,可以看看這篇文章,這是一篇介紹異步編程的文章,什么叫做“回調(diào)地獄”,可以看看下面的例子:
fs.readdir(source, function(err, files) { if (err) { console.log("Error finding files: " + err) } else { files.forEach(function(filename, fileIndex) { console.log(filename) gm(source + filename).size(function(err, values) { if (err) { console.log("Error identifying file size: " + err) } else { console.log(filename + " : " + values) aspect = (values.width / values.height) widths.forEach(function(width, widthIndex) { height = Math.round(width / aspect) console.log("resizing " + filename + "to " + height + "x" + height) this.resize(width, height).write(destination + "w" + width + "_" + filename, function(err) { if (err) console.log("Error writing file: " + err) }) }.bind(this)) } }) }) } })
是不是有種想吐的感覺(jué),一層一層的嵌套,雖說(shuō)這種嵌套十分正常,倘若每段代碼都是這樣的呈現(xiàn),相信二次開(kāi)發(fā)者一定會(huì)累死!關(guān)于如何解耦我就不細(xì)說(shuō)了,可以回頭看看上面那篇回調(diào)地獄的文章。
ECMAScript 6中有一個(gè) Generator 對(duì)象,過(guò)段時(shí)間會(huì)對(duì) ES6 中的新知識(shí)進(jìn)行一一的探討,這里不多說(shuō)了,有興趣的同學(xué)可以看看 H-Jin 寫(xiě)的一篇文章使用 (Generator) 生成器解決 JavaScript 回調(diào)嵌套問(wèn)題,使用 yield 關(guān)鍵詞和 Generator 把嵌套給“拉直”了,這種方式就像是 chrome 的 DevTool 中使用斷點(diǎn)一般,用起來(lái)特別舒服。
五、串并行的轉(zhuǎn)換留到下次說(shuō)吧,文字敲多了,累 :)
六、小結(jié)本文提到了異步編程的相關(guān)概念和使用中會(huì)遇到的問(wèn)題,在寫(xiě)文章之前做了三天的調(diào)研,不過(guò)還是有很多點(diǎn)沒(méi)說(shuō)全,下次對(duì)異步編程有了更深入的理解再來(lái)談一談。
七、參考資料Javascript異步編程的4種方法 阮一峰
javascript 異步編程 司徒正美
HTML Specification web develop group
Promise/A+ 規(guī)范
異步編程:When.js快速上手 JerrryQu
《Async Javascript》 By Trevor Burnham
非常有意義,卻尚未兼容的SharedWorker 次碳酸鈷
HTML5 Web Worker Franky
作者:Barret Lee
出處:http://barretlee.github.io/javascript-asynchronous-programming
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/85528.html
摘要:的翻譯文檔由的維護(hù)很多人說(shuō),阮老師已經(jīng)有一本關(guān)于的書(shū)了入門(mén),覺(jué)得看看這本書(shū)就足夠了。前端的異步解決方案之和異步編程模式在前端開(kāi)發(fā)過(guò)程中,顯得越來(lái)越重要。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(shū)(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書(shū)的目的是以目前還在制定中的ECMASc...
摘要:從最開(kāi)始的到封裝后的都在試圖解決異步編程過(guò)程中的問(wèn)題。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。寫(xiě)一個(gè)符合規(guī)范并可配合使用的寫(xiě)一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來(lái)處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問(wèn)題描述 在開(kāi)發(fā)過(guò)程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過(guò)http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過(guò)...
摘要:如果我們假設(shè)文件和文件位于相同的目錄,那么代碼是這樣的其他方法如下編程步驟創(chuàng)建對(duì)象設(shè)置請(qǐng)求方式調(diào)用回調(diào)函數(shù)發(fā)送請(qǐng)求處理返回的結(jié)果創(chuàng)建對(duì)象一般來(lái)說(shuō)手寫(xiě)的時(shí)候,首先需要判斷該瀏覽器是否支持對(duì)象,如果支持則創(chuàng)建該對(duì)象,如果不支持則創(chuàng)建對(duì)象。 Ajax的簡(jiǎn)介 什么是Ajax AJAX = Asynchronous JavaScript and XML(異步的 JavaScript 和 XML)...
摘要:函數(shù)會(huì)在之后的某個(gè)時(shí)刻觸發(fā)事件定時(shí)器。事件循環(huán)中的這樣一次遍歷被稱(chēng)為一個(gè)。執(zhí)行完畢并出棧。當(dāng)定時(shí)器過(guò)期,宿主環(huán)境會(huì)把回調(diào)函數(shù)添加至事件循環(huán)隊(duì)列中,然后,在未來(lái)的某個(gè)取出并執(zhí)行該事件。 原文請(qǐng)查閱這里,略有改動(dòng)。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第四章。 現(xiàn)在,我們將會(huì)通過(guò)回顧單線程環(huán)境下編程的弊端及如何克服這些困難以創(chuàng)建令人驚嘆...
閱讀 2757·2023-04-25 14:15
閱讀 2704·2021-11-04 16:11
閱讀 3395·2021-10-14 09:42
閱讀 442·2019-08-30 15:52
閱讀 2826·2019-08-30 14:03
閱讀 3546·2019-08-30 13:00
閱讀 2112·2019-08-26 11:40
閱讀 3308·2019-08-26 10:25