先說一下JavaScript異步的概念:
首先因?yàn)閖s是單線程的語言旗笔,且沒有異步的特性。
然后js的宿主環(huán)境是在瀏覽器端离例,js的異步操作是瀏覽器來提供的,瀏覽器的js引擎在執(zhí)行js代碼的時(shí)候也只是會(huì)提供一條線程去執(zhí)行悉稠,真摳...并且宫蛆!js(主棧列內(nèi)的js,后文解析什么是主棧列)代碼執(zhí)行會(huì)阻塞渲染線程和瀏覽器事件觸發(fā)線程的猛,后面這兩個(gè)線程是屬于瀏覽器內(nèi)的常駐線程耀盗,當(dāng)然還有其他的比如說http請求線程,那么比如說我們js代碼內(nèi)有一個(gè)是改變某個(gè)DOM元素樣式的代碼卦尊,執(zhí)行了之后只是記錄下這個(gè)渲染事件叛拷,不會(huì)去切換渲染線程進(jìn)行渲染,而是繼續(xù)執(zhí)行剩下的主棧列內(nèi)的代碼岂却,好煩..發(fā)現(xiàn)又要解析主棧列和任務(wù)隊(duì)列的概念..可以去看阮一峰的event loop解析忿薇,就是說,算了上代碼吧:
$.ajax({
type:'get',
url:url,
beforeSend:function() {
console.log('發(fā)送請求了,別看我在ajax內(nèi),其實(shí)跟它沒什么關(guān)系')
},
success:function(data){
console.log('ajax執(zhí)行完畢,我比setTimeout的事件優(yōu)先級(jí)要高')
},
})
setTimeout(function() {
console.log('定時(shí)器也完了')
},0)
console.log('我在主棧列躏哩,讓我先走')
//觸發(fā)順序
1.發(fā)送請求了,別看我在ajax內(nèi),其實(shí)跟它沒什么關(guān)系
2.我在主棧列署浩,讓我先走
3.jquery-1.9.1.min.js:5 XHR finished loading: GET "http://localhost:2000/123".
4.ajax執(zhí)行完畢
5.定時(shí)器也完了
js引擎在解析js代碼的時(shí)候,遇到settimeout和setInterval或者ajax都會(huì)先告訴它們緩緩扫尺,你們先進(jìn)任務(wù)隊(duì)列里面等等筋栋,我執(zhí)行完剩下在主棧列內(nèi)的代碼先。
然后執(zhí)行完主棧列的代碼之后正驻,js線程完事之后弊攘,并不會(huì)馬上去任務(wù)隊(duì)列內(nèi)找任務(wù)放到主棧列,而是..先查找剛剛是不是有需要渲染引擎要干的是..比如說你要改某個(gè)DOM元素的背景色姑曙,如果有的話襟交,那么就會(huì)切換渲染線程,先把頁面的渲染需要的repaint和reflow做了伤靠,關(guān)于repaint和reflow在這里不做陳述婿着,有興趣google一下吧。
那讓渲染引擎做完了渲染工作醋界,瀏覽器才回去查找任務(wù)隊(duì)列有沒有人掛號(hào)竟宋,采取的是輪詢機(jī)制,跟醫(yī)生看病人差不多形纺,就是說一個(gè)一個(gè)來丘侠,隨便說一句ajax比settimeout和setInterval優(yōu)先級(jí)要高的,比如說執(zhí)行完ajax事件1逐样,就回去看任務(wù)隊(duì)列蜗字?nonono打肝,先看有沒有渲染引擎要做的事,就是說再走一次剛剛主棧列的流程挪捕,沒有渲染的事了粗梭?那就下一位病人吧。
回到主題...為什么我們需要異步级零,因?yàn)閯倓偳拔乃f的js阻塞断医,如果說ajax是同步執(zhí)行的..那么就會(huì)等服務(wù)器響應(yīng)我的請求,拿到數(shù)據(jù)再做其他事情奏纪,那如果服務(wù)器不行請求很久那豈不是很low鉴嗤,那么瀏覽器干了什么就是在走到ajax的時(shí)候,在把它推到任務(wù)隊(duì)列之前序调,起一個(gè)http線程醉锅,去發(fā)送請求,請求完成了发绢,再講ajax事件推到任務(wù)隊(duì)列的末端硬耍,從而實(shí)現(xiàn)異步,在說一句就是根據(jù)上面所說的其實(shí)settimeout和setInterval要比預(yù)期的事件要多一點(diǎn)的边酒,因?yàn)樵陉?duì)列中默垄,不是即刻執(zhí)行,明天再更新甚纲。
好吧口锭,干貨來了,在日常的寫(lu)作(ma)中我們應(yīng)該怎么樣優(yōu)(zuo)雅(si)地利用異步機(jī)制去完成我們想要的異步操作呢:
(一)welcome to callback hell(回調(diào)函數(shù))
function getSomething(fn){
var num=0;
settimeout(function(){
num=1;
fn(num);
})
}
function compute(x) {
alert(x * 2);
}
getSomething(compute);//任性介杆,不喜歡大小寫命名法
如上文所說鹃操,settimeout內(nèi)的js代碼都進(jìn)入了主棧列,那么執(zhí)行順序就是從上至下了春哨,但是在復(fù)雜的需求下這種方法會(huì)產(chǎn)生callback hell荆隘,并且還不容易看出詳細(xì)異步步驟,不簡單的邏輯的話可以用用赴背。
(二)promise
function getSomething() {
var r = 0;
return new Promise(function(resolve) {
setTimeout(function() {
r = 2;
resolve(r);
}, 10);
});}
function compute(x) {
alert(x * 2);
}
getSomething().then(compute);
//沒有接觸Promise的朋友可以去看看Promise的使用椰拒,因?yàn)橐粫r(shí)半會(huì)又講不完...本文僅提供異步方法
(三)generator
function getSomething() {
var r = 0;
setTimeout(function() {
r = 2;
it.next(r);
}, 10);
}
function *compute(it) {
var x = yield getSomething();
alert(x * 2);
}
var it = compute();
it.next();
//我只負(fù)責(zé)提供demo......詳細(xì)可以去看es6的教程
(四)promise + generator
function getSomething() {
var r = 0;
return new Promise(function(resolve) {
setTimeout(function() {
r = 2;
resolve(r);
}, 10);
});
}
function *compute() {
var x = yield getSomething();
alert(x * 2);
}
var it = compute();it.next().value.then(function(value) {
it.next(value);
});
(五)async (ES6都玩膩了,ES7還遠(yuǎn)嗎凰荚,異步操作終極大法)
function getSomething() {
var r = 0;
return new Promise(function(resolve) {
setTimeout(function() {
r = 2;
resolve(r);
}, 10);
});}
async function compute() {
var x = await getSomething();
alert(x * 2);
}
compute();
困死本寶寶了..1點(diǎn)了燃观。
好咯,晚安便瑟,world peace.