一文搞懂Node.js以及瀏覽器中的事件循環(huán)機(jī)制
原文轉(zhuǎn)自:Node.js VS 瀏覽器以及事件循環(huán)機(jī)制 https://juejin.im/post/6871832597891121166
本文主要梳理node.js,瀏覽器相關(guān)及Event Loop事件循環(huán)等,會(huì)持續(xù)補(bǔ)充更新哦!首先我們要記住JS是一個(gè)單線(xiàn)程的語(yǔ)言。
JS同步異步
同步阻塞 **異步非阻塞:**在涉及需要等待的操作,我們選擇讓程序繼續(xù)運(yùn)行,在等待時(shí)間結(jié)束的時(shí)候,通知一下我們的程序內(nèi)容執(zhí)行完畢,你可以操作這些資源了,這段等待時(shí)間并不影響你程序的繼續(xù)執(zhí)行,只是在未來(lái)的某個(gè)時(shí)間段(不確定),有一個(gè)操作一定會(huì)執(zhí)行。
JS的異步方案演進(jìn)史
Raw Callback Style -> Promise Callback Style -> Generator Callback Style -> Async/Await Callback
任務(wù)隊(duì)列:先進(jìn)先出
JS Engine 和 JS Runtime
**Engine(執(zhí)行引擎):**如V8 Engine,V8 實(shí)現(xiàn)并提供了 ECMAScript 標(biāo)準(zhǔn)中的所有數(shù)據(jù)類(lèi)型、操作符、對(duì)象和方法(注意并沒(méi)有 DOM)。Event Loop 是屬于 JavaScript Runtime 的,是由宿主環(huán)境提供的(比如瀏覽器,node) **Runtime(執(zhí)行環(huán)境):**Chrome 提供了 window、DOM,而 Node.js 則是 require、process 等等。
JS執(zhí)行機(jī)制
事件循環(huán)(Event Loop)是js實(shí)現(xiàn)異步的一種方法,也是js的執(zhí)行機(jī)制。
同步和異步任務(wù)分別進(jìn)入不同的執(zhí)行"場(chǎng)所",同步的進(jìn)入主線(xiàn)程,異步的進(jìn)入Event Table并注冊(cè)函數(shù)。 當(dāng)指定的事情完成時(shí),Event Table會(huì)將這個(gè)函數(shù)移入Event Queue。 主線(xiàn)程內(nèi)的任務(wù)執(zhí)行完畢為空,會(huì)去Event Queue讀取對(duì)應(yīng)的函數(shù),進(jìn)入主線(xiàn)程執(zhí)行。 上述過(guò)程會(huì)不斷重復(fù),也就是常說(shuō)的Event Loop(事件循環(huán))。
怎么知道主線(xiàn)程執(zhí)行棧為空
js引擎存在monitoring process進(jìn)程,會(huì)持續(xù)不斷的檢查主線(xiàn)程執(zhí)行棧是否為空,一旦為空,就會(huì)去Event Queue那里檢查是否有等待被調(diào)用的函數(shù)。
執(zhí)行規(guī)則
首先在執(zhí)行棧(call stack)中的內(nèi)容執(zhí)行完畢清空后,會(huì)在事件隊(duì)列(Event queue)檢查一下哪些是宏任務(wù)哪些是微任務(wù),然后執(zhí)行所有的微任務(wù),然后執(zhí)行一個(gè)宏任務(wù),之后再次執(zhí)行所有的微任務(wù)。也就是說(shuō)在主線(xiàn)程(main thread)任務(wù)執(zhí)行完畢后會(huì)把任務(wù)隊(duì)列中的微任務(wù)全部執(zhí)行,然后再執(zhí)行一個(gè)宏任務(wù),這個(gè)宏任務(wù)執(zhí)行完再次檢查隊(duì)列內(nèi)部的微任務(wù),有就全部執(zhí)行沒(méi)有就再執(zhí)行一個(gè)宏任務(wù)。 JS是單線(xiàn)程但是瀏覽器是多線(xiàn)程。你的異步任務(wù)是瀏覽器開(kāi)啟對(duì)應(yīng)的線(xiàn)程來(lái)執(zhí)行的,最后放入JS引擎中進(jìn)行執(zhí)行。 所以在執(zhí)行定時(shí)器、事件、ajax這些異步事件的時(shí)候是另外三個(gè)線(xiàn)程在執(zhí)行代碼,并不是JS引擎在做事情,在這些線(xiàn)程達(dá)到某一特定事件把任務(wù)放入JS引擎的線(xiàn)程中,同時(shí)GUI線(xiàn)程(渲染界面HTMl的線(xiàn)程)與JS線(xiàn)程是互斥的,在JS引擎執(zhí)行時(shí)GUI線(xiàn)程會(huì)被凍結(jié)、掛起。
瀏覽器主線(xiàn)程常駐線(xiàn)程
GUI 渲染線(xiàn)程
繪制頁(yè)面,解析 HTML、CSS,構(gòu)建 DOM 樹(shù),布局和繪制等 頁(yè)面重繪和回流 與 JS 引擎線(xiàn)程互斥,也就是所謂的 JS 執(zhí)行阻塞頁(yè)面更新 JS 引擎線(xiàn)程
負(fù)責(zé) JS 腳本代碼的執(zhí)行 負(fù)責(zé)執(zhí)行準(zhǔn)備好待執(zhí)行的事件,即定時(shí)器計(jì)數(shù)結(jié)束,或異步請(qǐng)求成功并正確返回的事件 與 GUI 渲染線(xiàn)程互斥,執(zhí)行時(shí)間過(guò)長(zhǎng)將阻塞頁(yè)面的渲染 事件觸發(fā)線(xiàn)程
負(fù)責(zé)將準(zhǔn)備好的事件交給 JS 引擎線(xiàn)程執(zhí)行 多個(gè)事件加入任務(wù)隊(duì)列的時(shí)候需要排隊(duì)等待(JS 的單線(xiàn)程) 定時(shí)器觸發(fā)線(xiàn)程
負(fù)責(zé)執(zhí)行異步的定時(shí)器類(lèi)的事件,如 setTimeout、setInterval 定時(shí)器到時(shí)間之后把注冊(cè)的回調(diào)加到任務(wù)隊(duì)列的隊(duì)尾 HTTP 請(qǐng)求線(xiàn)程
負(fù)責(zé)執(zhí)行異步請(qǐng)求 主線(xiàn)程執(zhí)行代碼遇到異步請(qǐng)求的時(shí)候會(huì)把函數(shù)交給該線(xiàn)程處理,當(dāng)監(jiān)聽(tīng)到狀態(tài)變更事件,如果有回調(diào)函數(shù),該線(xiàn)程會(huì)把回調(diào)函數(shù)加入到任務(wù)隊(duì)列的隊(duì)尾等待執(zhí)行
宏任務(wù)和微任務(wù)
macro-task(宏任務(wù)):包括整體代碼script,setTimeout,setInterval,I/O、UI Rendering等 micro-task(微任務(wù)):Promise.then catch finally(注意不是說(shuō) Promise,new promise直接執(zhí)行),process.nextTick,MutationObserver
setTimeout(fn,0)
指定某個(gè)任務(wù)在主線(xiàn)程最早可得的空閑時(shí)間執(zhí)行,意思就是不用再等多少秒了,只要主線(xiàn)程執(zhí)行棧內(nèi)的同步任務(wù)全部執(zhí)行完成,棧為空就馬上執(zhí)行。(關(guān)于setTimeout要補(bǔ)充的是,即便主線(xiàn)程為空,0毫秒實(shí)際上也是達(dá)不到的。根據(jù)HTML的標(biāo)準(zhǔn),最低是4毫秒。)
setInterval(fn,0)
會(huì)每隔指定的時(shí)間將注冊(cè)的函數(shù)置入Event Queue,如果前面的任務(wù)耗時(shí)太久,那么同樣需要等待。每過(guò)ms秒,會(huì)有fn進(jìn)入Event Queue。一旦setInterval的回調(diào)函數(shù)fn執(zhí)行時(shí)間超過(guò)了延遲時(shí)間ms,那么就完全看不出來(lái)有時(shí)間間隔了。
requestAnimationFrame
**請(qǐng)求動(dòng)畫(huà)幀,是一個(gè)宏任務(wù),**html5 提供的一個(gè)專(zhuān)門(mén)用于請(qǐng)求動(dòng)畫(huà)的API,相比起setTimeout由系統(tǒng)決定回調(diào)函數(shù)的執(zhí)行時(shí)機(jī)。60Hz的刷新頻率,那么每次刷新的間隔中會(huì)執(zhí)行一次回調(diào)函數(shù),不會(huì)引起丟幀,不會(huì)卡頓。
Promise與process.nextTick(callback)
process.nextTick(callback):類(lèi)似node.js版的"setTimeout",在事件循環(huán)的下一次循環(huán)中調(diào)用 callback 回調(diào)函數(shù)。
不同類(lèi)型的任務(wù)會(huì)進(jìn)入對(duì)應(yīng)的Event Queue,比如setTimeout和setInterval會(huì)進(jìn)入相同的Event Queue。
執(zhí)行和運(yùn)行的區(qū)別
執(zhí)行和運(yùn)行有很大的區(qū)別,javascript在不同的環(huán)境下,比如node,瀏覽器,Ringo等等,執(zhí)行方式是不同的。而運(yùn)行大多指javascript解析引擎,是統(tǒng)一的。
一些特殊的點(diǎn)
async 隱式返回 Promise 作為結(jié)果,執(zhí)行完 await 之后直接跳出 async 函數(shù),讓出執(zhí)行的所有權(quán),當(dāng)前任務(wù)的其他代碼執(zhí)行完之后再次獲得執(zhí)行權(quán)進(jìn)行執(zhí)行 立即 resolve 的 Promise 對(duì)象,是在本輪"事件循環(huán)"的結(jié)束時(shí)執(zhí)行,而不是在下一輪"事件循環(huán)"的開(kāi)始時(shí) 在一輪event loop中多次修改同一dom,只有最后一次會(huì)進(jìn)行繪制。 渲染更新(Update the rendering)會(huì)在event loop中的tasks和microtasks完成后進(jìn)行,但并不是每輪event loop都會(huì)更新渲染,這取決于是否修改了dom和瀏覽器覺(jué)得是否有必要在此時(shí)立即將新?tīng)顟B(tài)呈現(xiàn)給用戶(hù)。如果在一幀的時(shí)間內(nèi)(時(shí)間并不確定,因?yàn)闉g覽器每秒的幀數(shù)總在波動(dòng),16.7ms只是估算并不準(zhǔn)確)修改了多處dom,瀏覽器可能將變動(dòng)積攢起來(lái),只進(jìn)行一次繪制,這是合理的。 如果希望在每輪event loop都即時(shí)呈現(xiàn)變動(dòng),可以使用requestAnimationFrame。
Node下的 Event Loop
基于libuv實(shí)現(xiàn),而libuv是 Node 的新跨平臺(tái)抽象層,libuv使用異步IO事件驅(qū)動(dòng)的編程方式,核心是提供i/o的事件循環(huán)和異步回調(diào)。libuv的API包含有時(shí)間,非阻塞的網(wǎng)絡(luò),異步文件操作,子進(jìn)程等等。
六個(gè)階段
timers:執(zhí)行setTimeout() 和 setInterval()中到期的callback。 pending callback: 上一輪循環(huán)中有少數(shù)的I/O callback會(huì)被延遲到這一輪的這一階段執(zhí)行 idle, prepare:僅內(nèi)部使用 poll: 最為重要的階段,執(zhí)行I/O callback,在適當(dāng)?shù)臈l件下會(huì)阻塞在這個(gè)階段 check: 執(zhí)行setImmediate的callback close callbacks: 執(zhí)行close事件的callback,例如socket.on('close'[,fn])、http.server.on('close, fn)
Node與瀏覽器的 Event Loop 差異
瀏覽器環(huán)境下,microtask的任務(wù)隊(duì)列是每個(gè)macrotask執(zhí)行完之后執(zhí)行。 而在Node.js中,microtask會(huì)在事件循環(huán)的各個(gè)階段之間執(zhí)行,也就是一個(gè)階段執(zhí)行完畢,就會(huì)去執(zhí)行microtask隊(duì)列的任務(wù)。
node.js 是?個(gè) JS 的服務(wù)端運(yùn)?環(huán)境,簡(jiǎn)單的來(lái)說(shuō),是在 JS 語(yǔ)?規(guī)范的基礎(chǔ)上,封裝了?些服務(wù)端的運(yùn)?時(shí) 對(duì)象,讓我們能夠簡(jiǎn)單實(shí)現(xiàn)?常多的業(yè)務(wù)功能。> 基于 JS 語(yǔ)法增加與操作系統(tǒng)之間的交互。
Node的簡(jiǎn)單介紹
底層依賴(lài)
node.js 的主要依賴(lài)?模塊有以下內(nèi)容:
V8 引擎:主要是 JS 語(yǔ)法的解析,有了它才能識(shí)別JS語(yǔ)法 libuv: c 語(yǔ)?實(shí)現(xiàn)的?個(gè)?性能異步?阻塞 IO 庫(kù),?來(lái)實(shí)現(xiàn) node.js 的事件循環(huán) http-parser/llhttp: 底層處理 http 請(qǐng)求,處理報(bào)?,解析請(qǐng)求包等內(nèi)容 openssl: 處理加密算法,各種框架運(yùn)??泛 zlib: 處理壓縮等內(nèi)容
常見(jiàn)內(nèi)置模塊
fs: ?件系統(tǒng),能夠讀取寫(xiě)?當(dāng)前安裝系統(tǒng)環(huán)境中硬盤(pán)的數(shù)據(jù) path: 路徑系統(tǒng),能夠處理路徑之間的問(wèn)題 crypto: 加密相關(guān)模塊,能夠以標(biāo)準(zhǔn)的加密?式對(duì)我們的內(nèi)容進(jìn)?加解密 dns: 處理 dns 相關(guān)內(nèi)容,例如我們可以設(shè)置 dns 服務(wù)器等等 http: 設(shè)置?個(gè) http 服務(wù)器,發(fā)送 http 請(qǐng)求,監(jiān)聽(tīng)響應(yīng)等等 readline: 讀取 stdin 的??內(nèi)容,可以讀取、增加、刪除我們命令?中的內(nèi)容 os: 操作系統(tǒng)層?的?些 api,例如告訴你當(dāng)前系統(tǒng)類(lèi)型及?些參數(shù) vm: ?個(gè)專(zhuān)?處理沙箱的虛擬機(jī)模塊,底層主要來(lái)調(diào)? v8 相關(guān) api 進(jìn)?代碼解析。
Buffer 緩沖
Buffer 類(lèi),用來(lái)創(chuàng)建一個(gè)專(zhuān)門(mén)存放二進(jìn)制數(shù)據(jù)的緩存區(qū)。
Buffer 是 UInt8Array 是數(shù)組,且每個(gè)item的有效范圍是 0~255(無(wú)符號(hào)8位) 詳細(xì)api可查 [nodejs.cn/api/buffer.…][nodejs.cn_api_buffer.]
????Buffer.from([1,?1,?1,?1]);?//Buffer.from()?接口創(chuàng)建Buffer對(duì)象(傳入的array的元素只能是數(shù)字,不然就會(huì)自動(dòng)被0覆蓋)
????Buffer.from([257,?257.5,?-255,?'1']);?//都是1
????Buffer.from('abcd');?//utf8編碼轉(zhuǎn)換?
????const?bf?=?Buffer.alloc(256);?//創(chuàng)建一個(gè)長(zhǎng)度為?256、且用?0?填充的?Buffer
????const?len?=?buf.write("www.baidu.com");?//寫(xiě)入,返回實(shí)際寫(xiě)入的大小。如果 buffer 空間不足,?則只會(huì)寫(xiě)入部分字符串。
????const?str1?=?buf.toString('utf8')?////?使用?'utf8'?編碼,?并輸出:?www.baidu.com
????const?str2?=?buf.toString('utf8',0,9)?////?使用?'utf8'?編碼,?并輸出:?www.baidu
EventEmitter 事件
events 模塊只提供了一個(gè)對(duì)象:events.EventEmitter。EventEmitter 的核心就是事件觸發(fā)與事件監(jiān)聽(tīng)器功能的封裝。
**on方法,**注冊(cè)事件回調(diào) **emit方法,**手動(dòng)觸發(fā)事件 詳細(xì)api可查** **[nodejs.cn/api/events.…][nodejs.cn_api_events.]
????const?EventEmitter?=?require('events');
????class?MyEventEmitter?extends?EventEmitter?{}
????const?myEventEmitter?=?new?MyEventEmitter();
????myEventEmitter.on('ping',?function()?{
?????console.log('pong');
????})
????myEventEmitter.emit('ping');
Stream 流
Stream 是一個(gè)抽象接口,Node 中有很多對(duì)象實(shí)現(xiàn)了這個(gè)接口。例如,對(duì)http 服務(wù)器發(fā)起請(qǐng)求的request 對(duì)象就是一個(gè) Stream,還有stdout(標(biāo)準(zhǔn)輸出)
Stream 有四種流類(lèi)型
Readable - 可讀操作 Writable - 可寫(xiě)操作 Duplex - 可讀可寫(xiě)操作 Transform - 操作被寫(xiě)入數(shù)據(jù),然后讀出結(jié)果 Stream 對(duì)象本身就是一個(gè) EventEmitter ,常用事件有
data - 當(dāng)有數(shù)據(jù)可讀時(shí)觸發(fā)。
end - 沒(méi)有更多的數(shù)據(jù)可讀時(shí)觸發(fā)。
error - 在接收和寫(xiě)入過(guò)程中發(fā)生錯(cuò)誤時(shí)觸發(fā)。
finish - 所有數(shù)據(jù)已被寫(xiě)入到底層系統(tǒng)時(shí)觸發(fā)。
Stream 內(nèi)部含有 Buffer
Stream優(yōu)勢(shì):只會(huì)先讀文件需要的一部分,內(nèi)存損耗較小
詳細(xì)api可查** **[nodejs.cn/api/stream.…][nodejs.cn_api_stream.]
var readerStream = fs.createReadStream('input.txt',{start:50,end:99}); //讀取一部分
常見(jiàn)全局對(duì)象
setTimeout 創(chuàng)建定時(shí) clearTimeout(t) 停止定時(shí) setInterval 創(chuàng)建輪詢(xún) clearInterval(t) 停止輪詢(xún) console 打印 process 進(jìn)程
模塊全局對(duì)象
模塊加載時(shí)注入
__filename 模塊文件的路徑 __dirname 當(dāng)前執(zhí)行腳本所在的目錄 exports 輸出 module 模塊 require 請(qǐng)求
npm(node package manager)
node.js 內(nèi)置的用于安裝和發(fā)布符合 node.js 標(biāo)準(zhǔn)的模塊的?款?具,從?實(shí)現(xiàn)社區(qū)共建的?的繁榮整個(gè)社區(qū)。
npx 是 npm@5 之后新增的?個(gè)命令,它使得我們可以 在不安裝模塊到當(dāng)前環(huán)境的前提下,使??些 cli 功能,每次調(diào)用都會(huì)使用最新的版本。
全局安裝了 vue
npm i -g vue vue init webpack test
?論是項(xiàng)?中還是全局都沒(méi)有安裝 vue (但實(shí)際上是安裝了的,但表現(xiàn)確實(shí)像沒(méi)有安裝)
npx vue test
發(fā)布一個(gè)npm包
安裝webpack簡(jiǎn)易框架(這里以發(fā)布vue插件為例)
????npm?install?-g?@vue/cli-init?//cli版本是3及以上,vue?init?的運(yùn)行效果將會(huì)跟[email protected]?相同
????vue?init?webpack-simple?marquee
????//安裝完成目錄結(jié)構(gòu)
????文件目錄/
????├──?index.html
????├──?package.json
????├──?README.md
????├──?.babelrc
????├──?.editorconfig
????├──?.gitignore
????├──?src
????│???├──?App.vue
????│???├──?assets
????│???│???└──?logo.png
????│???└──?main.js
????└──?webpack.config.js
封裝Vue插件(創(chuàng)建一個(gè)index.js)
//在APP.vue中查看效果 npm install npm run dev
在index.js中export封裝好的Vue插件
修改webpack.config.js
????const?NODE_ENV?=?process.env.NODE_ENV;
????module.exports?=?{
??????entry:?NODE_ENV?==?'development'???'./src/main.js'?:?'./src/marquee/index.js',
??????output:?{
????????path:?path.resolve(__dirname,?'./dist'),
????????publicPath:?'/dist/',
????????filename:?'marquee.js',?//輸出文件名
????????library:?'marquee',?//?指定的就是你使用require時(shí)的模塊名
????????libraryTarget:?'umd', //?指定輸出格式, UMD 同時(shí)支持兩種執(zhí)行環(huán)境:node環(huán)境、瀏覽器環(huán)境。
????????umdNamedDefine:?true?//?會(huì)對(duì) UMD 的構(gòu)建過(guò)程中的 AMD 模塊進(jìn)行命名。否則就使用匿名的 define
??????},
????}
打包
????npm?run?build
????//出現(xiàn)dist文件夾
修改package.json
????{
?????"author":?"maomincoding",??//author的值為npm用戶(hù)名,這里一定要注意
??????"main":?"dist/marquee.js",?//main的值為剛才打包的路徑文件
??????"license":?"ISC",?//license的值按照以上即可
??????"keywords":?["marquee"],?//keywords為用戶(hù)搜索的關(guān)鍵詞
??????"private":?false,?//private設(shè)為false,?開(kāi)源因此需要將這個(gè)字段改為?false
????}
發(fā)包文件名單
????//npm發(fā)包默認(rèn)包含的文件(不區(qū)分大小寫(xiě))
????package.json
????README?(and?its?variants)
????CHANGELOG?(and?its?variants)
????LICENSE?/?LICENCE
????package.json屬性main指向的文件
????//npm發(fā)包默認(rèn)忽略的文件
????.git
????CVS
????.svn
????.hg
????.lock-wscript
????.wafpickle-N
????.*.swp
????.DS_Store
????._*
????npm-debug.log
????.npmrc
????node_modules
????config.gypi
????*.orig
????package-lock.json?(use?shrinkwrap?instead)
????//發(fā)包白名單,設(shè)置package.json中的files屬性
????files:["package.json","src"]
????//發(fā)包黑名單,通過(guò)下面兩個(gè)文件來(lái)設(shè)置忽略的文件或文件夾
????.gitignore
????.npmignore
????//文件設(shè)置優(yōu)先級(jí)!!!
????files>.npmignore>.gitignore
編輯README.md npm包發(fā)布
????npm?config?get?registry?//查看登錄源
????npm?config?set?registry=http://registry.npmjs.org?//如果不是http://registry.npmjs.org就切換一下
????npm?login?//登錄?回車(chē)出現(xiàn)?Logged?in?as?maomincoding?on?http://registry.npmjs.org,那么就成功了
??? npm publish //成功!
npm包撤銷(xiāo) :只有在發(fā)包的24小時(shí)內(nèi)才允許撤銷(xiāo),以后發(fā)包的時(shí)候也不能再和被撤銷(xiāo)的包的名稱(chēng)和版本重復(fù)了,建議慎重!I sure hope you know what you are doing
????npm?unpublish?包名?--force?//撤銷(xiāo)
npm包更新
修復(fù)bug,小改動(dòng),c加1 增加了新特性,但仍能向后兼容,b加1 有很大的改動(dòng),無(wú)法向后兼容,a加1 打開(kāi)根目錄下的package.json找到version字段 "version":"a.b.c"
再次發(fā)布
nvm(node version manager)
管理 node 版本的?個(gè)?具,簡(jiǎn)單來(lái)說(shuō),就是通過(guò)將多個(gè) node 版本安裝在指定路徑,然后通過(guò) nvm 命令切換時(shí),就會(huì)切換我們環(huán)境變量中 node 命令指定的實(shí)際執(zhí)?的軟件路徑。
nrm(npm registry manager )
是npm的鏡像源管理工具,使用這個(gè)可以快速地在 npm 源間切換。
服務(wù)端框架 express/koa
node.js 內(nèi)部有?常多的內(nèi)置模塊,其中就有 http 模塊,express/koa 實(shí)際上就是 對(duì)這個(gè) http 模塊的再封裝,增加了中間件策略和其他 各種路由的通?處理,讓我們寫(xiě)起來(lái)更加?便。
body-parser express處理body的中間件 cookie-parser express處理cookie的中間件 中間件可以這樣理解,對(duì)于需要多次書(shū) 寫(xiě)的業(yè)務(wù)邏輯,可以使??種切?的形式,對(duì)相同邏輯進(jìn)?通?處理。 **洋蔥模型:**中間件線(xiàn)性的連貫的,自上而下(垂直)依次進(jìn)行劫持。執(zhí)行到next()會(huì)跳出當(dāng)前繼續(xù)向下一個(gè)中間件執(zhí)行,結(jié)束后會(huì)返回之前中間件執(zhí)行next()后面內(nèi)容。 koa和express在同步場(chǎng)景下完全相同,處理異步時(shí)koa進(jìn)行rosolve能正確執(zhí)行回調(diào)順序,但express缺少這個(gè)方法,會(huì)產(chǎn)生一些順序問(wèn)題。
周邊工具簡(jiǎn)介
quickjs
quickjs 是?個(gè) JS 的解析引擎,輕量代碼量也不?,與之功能類(lèi)似的就是 V8 引擎。
他最?的特點(diǎn)就是,?常?常輕量,這點(diǎn)從源碼中也能提現(xiàn),事實(shí)上并沒(méi)有太多的代碼,它的主要特點(diǎn)和優(yōu)勢(shì):
輕量?且易于嵌?:只需?個(gè)C?件,沒(méi)有外部依賴(lài),?個(gè)x86下的簡(jiǎn)單的“hello world”程序只要180KiB。 具有極低啟動(dòng)時(shí)間的快速解釋器:在?臺(tái)單核的臺(tái)式PC上,?約在100秒內(nèi)運(yùn)?ECMAScript 測(cè)試套件156000次。運(yùn)?時(shí)實(shí)例的完整?命周期在不到300微秒的時(shí)間內(nèi)完成。 ?乎完整實(shí)現(xiàn)ES2019?持,包括:模塊,異步?成器和和完整Annex B?持 (傳統(tǒng)的Web兼容性)。許多ES2020中帶來(lái)的特性也依然會(huì)被?持。 通過(guò)100%的ECMAScript Test Suite測(cè)試。 可以將Javascript源編譯為沒(méi)有外部依賴(lài)的可執(zhí)??件。
deno
deno 是?類(lèi)類(lèi)似于 node.js 的 JS 運(yùn)?時(shí)環(huán)境,同時(shí)他 也是由 node.js 之???打造出來(lái)的,他和 node.js ? 有什么區(qū)別呢?
相同點(diǎn):
deno 也是基于 V8 ,上層封裝?些系統(tǒng)級(jí)別的調(diào)? 我們的 deno 應(yīng)?也可以使? JS 開(kāi)發(fā) 不同點(diǎn):
deno 基于 rust 和 typescript 開(kāi)發(fā)?些上層模塊,所 以我們可以直接在 deno 應(yīng)?中書(shū)寫(xiě) ts deno ?持從 url 加載模塊,同時(shí)?持 top level await 等特性
sequelize ORM 框架
幫助我們抹平了 底層數(shù)據(jù)庫(kù)的細(xì)節(jié),我們使?這類(lèi)框架,就能按照它的 語(yǔ)法進(jìn)?書(shū)寫(xiě),最終?成能夠應(yīng)?于各個(gè)平臺(tái)的 sql 語(yǔ)句。
pm2 服務(wù)部署
使? pm2 啟動(dòng)服務(wù)端、進(jìn)?運(yùn)維
????npm?install?-g?pm2
????pm2?start?ws-server.js?—name?my-server
????pm2?list
????pm2?monit
????pm2?logs?ws-server.js
問(wèn)幾個(gè)問(wèn)題
Q1:下面哪幾種寫(xiě)法可以正確導(dǎo)出(commonJS)
????module.exports='hello?word'?//√
????exports.key='hello?word'?//√
????exports='hello?word'?//×
exports是module.exports值的引用,直接更改時(shí)引用地址進(jìn)行了改變,不會(huì)對(duì)module.exports產(chǎn)生影響
Q2:自測(cè)一下
????console.log('script?start');
????
????setTimeout(function()?{
??????console.log('setTimeout');
????},?0);
????
????Promise.resolve().then(function()?{
??????console.log('promise1');
????}).then(function()?{
??????console.log('promise2');
????});
????console.log('script?end');
script start->script end->promise1->promise2->setTimeout
Q3:再測(cè)一下
????console.log('script?start')
????
????async?function?async1()?{
??????await?async2()
??????console.log('async1?end')
????}
????async?function?async2()?{
??????console.log('async2?end')?
????}
????async1()
????
????setTimeout(function()?{
??????console.log('setTimeout')
????},?0)
????
????new?Promise(resolve?=>?{
??????console.log('Promise')
??????resolve()
????})
??????.then(function()?{
????????console.log('promise1')
??????})
??????.then(function()?{
????????console.log('promise2')
??????})
????
????console.log('script?end')
script start->async2 end->Promise->script end->async1 end->promise1->promise2->setTimeout
巨人的肩膀
[npm發(fā)布包以及更新包還有需要注意的幾點(diǎn)問(wèn)題(這里以發(fā)布vue插件為例)][npm_vue] [你好,JavaScript異步編程---- 理解JavaScript異步的美妙][JavaScript_---- _JavaScript] [這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制][JavaScript] [你不知道的 Event Loop][Event Loop] [【THE LAST TIME】徹底吃透 JavaScript 執(zhí)行機(jī)制][THE LAST TIME_ JavaScript] [深入理解 JavaScript Event Loop][JavaScript Event Loop] [從event loop規(guī)范探究javaScript異步及瀏覽器更新渲染時(shí)機(jī)][event loop_javaScript] [一次弄懂Event Loop(徹底解決此類(lèi)面試問(wèn)題)][Event Loop 1] [瀏覽器與Node的事件循環(huán)(Event Loop)有何區(qū)別?][Node_Event Loop]**
