今天和一個(gè)正在學(xué)習(xí)前端的朋友聊天 , 學(xué)習(xí)到了node.js這個(gè)內(nèi)容 。
首先呢 达传, node.js 是一個(gè)基于 Chrome V8 引擎的 JavaScript 運(yùn)行環(huán)境碌更。 node.js 使用了一個(gè)事件驅(qū)動(dòng)瓶籽、非阻塞式 I/O 的模型恃鞋,使其輕量又高效崖媚。
等等之類的 ...
最后他問(wèn)了我一道題目
console.log('111');
setTimeout(()=>{
console.log('222')
},1000);
console.log('333');
setTimeout(()=>{
console.log('444')
},0);
console.log(555);
就是執(zhí)行這樣一段js代碼 , 打印出來(lái)的結(jié)果是 :
111 -> 333 -> 555 -> 444 -> 222
(結(jié)果就是我們理論起來(lái) setTimeout 到底是同步還是異步的 ?為什么會(huì)這樣執(zhí)行 山宾? JavaScript不是一門單線程的語(yǔ)言嗎?)
下面來(lái)給大家分析一下(如果錯(cuò)誤 鳍徽,歡迎糾正)
一资锰、為什么JavaScript是單線程?
javascript語(yǔ)言的最大的特點(diǎn)之一就是單線程 阶祭,意思就是 :只能同一時(shí)間執(zhí)行同一段代碼绷杜。就比如說(shuō): 我這一句循環(huán)代碼需要5s來(lái)執(zhí)行 直秆,那么后面所有的代碼都要等著我這個(gè)循環(huán)結(jié)束才能繼續(xù)執(zhí)行 。
那么鞭盟,為什么JavaScript不能像Java一樣有多個(gè)線程呢圾结?
這是因?yàn)楦@門語(yǔ)言的用途有關(guān)。JavaScript作為瀏覽器腳本語(yǔ)言齿诉,主要用途是與用戶互動(dòng)筝野,以及操作DOM。這就決定了它只能是單線程 粤剧,否則會(huì)帶來(lái)很多復(fù)雜的同步問(wèn)題歇竟。為了避免復(fù)雜性,這個(gè)特性我相信將來(lái)也不會(huì)改變抵恋。
最新的HTML5提出了Web Worker標(biāo)準(zhǔn)焕议,允許JS腳本創(chuàng)建多個(gè)線程,但是子線程完全受主線程控制弧关,不能操作DOM盅安。所以JS還是單線程。
二世囊、為什么上述代碼會(huì)這樣執(zhí)行 别瞭?
這就需要了解 任務(wù)隊(duì)列
我們把需要執(zhí)行的代碼看成一個(gè)個(gè)任務(wù),把任務(wù)分成兩種茸习,同步任務(wù)(sknchronous)畜隶,異步任務(wù)(asynchronous)。
下面是它們的運(yùn)行機(jī)制:
1.所有同步任務(wù)都在主線程上号胚,形成一個(gè)執(zhí)行棧
2.主線程之外還有一個(gè)“任務(wù)隊(duì)列”籽慢,只要異步任務(wù)有了運(yùn)行結(jié)果 ,就在任務(wù)隊(duì)列中放一個(gè)事件
3.當(dāng)執(zhí)行棧中所有的任務(wù)執(zhí)行完了猫胁,就去看看任務(wù)隊(duì)列中有沒(méi)有需要執(zhí)行的事件 箱亿,如果有的話,就結(jié)束它們的等待弃秆,進(jìn)入執(zhí)行棧 届惋,開始執(zhí)行。
4.主線程不斷重讀上面三步
(這里還是單線程菠赚,只是多了一個(gè)任務(wù)隊(duì)列)
三脑豹、Event Loop
主線程從"任務(wù)隊(duì)列"中讀取事件,這個(gè)過(guò)程是循環(huán)不斷的衡查,所以整個(gè)的這種運(yùn)行機(jī)制又稱為Event Loop(事件循環(huán))瘩欺。這是計(jì)算機(jī)系統(tǒng)的一種運(yùn)行機(jī)制。
JavaScript語(yǔ)言就采用這種機(jī)制,來(lái)解決單線程運(yùn)行帶來(lái)的一些問(wèn)題俱饿。
上圖中歌粥,主線程運(yùn)行的時(shí)候,產(chǎn)生堆和棧拍埠,棧中的代碼調(diào)用各種外部API失驶,它們?cè)?任務(wù)隊(duì)列"中加入各種事件。只要棧中的代碼執(zhí)行完畢枣购,主線程就會(huì)去讀取"任務(wù)隊(duì)列"嬉探,依次執(zhí)行那些事件所對(duì)應(yīng)的回調(diào)函數(shù)。
執(zhí)行棧中的代碼(同步任務(wù))坷虑,總是在讀取"任務(wù)隊(duì)列"(異步任務(wù))之前執(zhí)行甲馋。
四、setTimeout 到底是同步還是異步的 迄损?
除了放置異步任務(wù)的事件定躏,"任務(wù)隊(duì)列"還可以放置定時(shí)事件。
setTimeout()接受兩個(gè)參數(shù)芹敌,第一個(gè)是回調(diào)函數(shù)痊远,第二個(gè)是推遲執(zhí)行的毫秒數(shù)。
需要注意的是氏捞,setTimeout()只是將事件插入了"任務(wù)隊(duì)列"碧聪,必須等到當(dāng)前代碼(執(zhí)行棧)執(zhí)行完,主線程才會(huì)去執(zhí)行它指定的回調(diào)函數(shù)液茎。要是當(dāng)前代碼耗時(shí)很長(zhǎng)逞姿,有可能要等很久,所以并沒(méi)有辦法保證捆等,回調(diào)函數(shù)一定會(huì)在setTimeout()指定的時(shí)間執(zhí)行滞造。
綜上所屬
setTimeout是單線程,類似異步栋烤,但不是異步 谒养。
最后在這里提一句 :
1.異步的三種實(shí)現(xiàn)方式
1) 回調(diào)函數(shù)
回調(diào)函數(shù)不一定是異步 , 但異步一定有回調(diào)函數(shù)
2) 事件
3) promise 承諾對(duì)象