成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

Nodejs高性能原理(下) --- 事件循環(huán)詳解

newsning / 1457人閱讀

摘要:如果一個即時定時器是被一個正在執(zhí)行的回調(diào)排入隊(duì)列的,則該定時器直到下一次事件循環(huán)迭代才會被觸發(fā)。參數(shù)描述在事件循環(huán)的當(dāng)前回合結(jié)束時要調(diào)用的函數(shù)。事件輪詢隨后的調(diào)用,會在任何事件包括定時器之前運(yùn)行。

系列文章

Nodejs高性能原理(上) --- 異步非阻塞事件驅(qū)動模型
Nodejs高性能原理(下) --- 事件循環(huán)詳解

前言

終于開始我nodejs的博客生涯了,先從基本的原理講起.以前寫過一篇瀏覽器執(zhí)行機(jī)制的文章,和nodejs的相似之處還是挺多的,不熟悉可以去看看先.
Javascript執(zhí)行機(jī)制--單線程,同異步任務(wù),事件循環(huán)

寫下來之后可能還是有點(diǎn)懞,以后慢慢補(bǔ)充,也歡迎指正,特別是那篇翻譯文章后面已經(jīng)看不懂了.有人出手科普一下就好了.因?yàn)閼械脛邮肿?整篇文章的圖片要么來源官網(wǎng),要么來源百度圖片.
補(bǔ)充: 當(dāng)前Nodejs版本10.3.0
2019/8/13 修改部分描述內(nèi)容

Nodejs事件循環(huán)詳解

基本來自The Node.js Event Loop, Timers, and process.nextTick(),可以說這部分我就是翻譯功能,部分翻譯太繞口會和諧一下,基本忠于原文.

當(dāng)nodejs開始運(yùn)行的時候會初始化事件循環(huán),處理所提供的輸入腳本或者放置進(jìn)REPL(Read Eval Print Loop:交互式解釋器類似 Window 系統(tǒng)的終端或 Unix/Linux shell),可能會進(jìn)行異步API調(diào)用.定時器調(diào)度,或者process.nextTick(),然后開始處理事件循環(huán)的流程.

下面來自官網(wǎng)的炫酷流程代碼示意圖(官網(wǎng)直接用符號拼湊出來,這里因?yàn)榫庉嬈鲉栴}衹能截圖)

注意: 每個框都被稱為事件循環(huán)的一個流程階段.

每個階段都有一個FIFO(先進(jìn)先出)執(zhí)行回調(diào)函數(shù)的隊(duì)列,然而每個階段都有其獨(dú)特之處.通常當(dāng)事件循環(huán)進(jìn)入到給定階段會執(zhí)行特定于該階段的所有操作.然后執(zhí)行該階段隊(duì)列的回調(diào)事件直到隊(duì)列耗盡或者超過最大執(zhí)行限度為止,然后事件循環(huán)就會走向下一階段,以此類推.

因?yàn)檫@些操作可能會調(diào)度更多的操作并且在poll階段中新的處理事件會加入到內(nèi)核的隊(duì)列,即處理輪詢事件時候又加入新的輪詢事件,因此,長時間運(yùn)行回調(diào)事件會讓poll階段運(yùn)行時間超過定時器的閾值.

階段綜述:

timers(定時器): 這階段執(zhí)行setTimeoutsetInterval調(diào)度的回調(diào);

pending callbacks(等待回調(diào)): 推遲到下一次循環(huán)迭代執(zhí)行I/O回調(diào);

idle,prepare(閑置,準(zhǔn)備): 只能內(nèi)部使用;

poll(輪詢): 檢索新的I/O事件;執(zhí)行I/O相關(guān)回調(diào)(除了close callbacks以外,大多數(shù)是定時器調(diào)度,和setImmediate()),當(dāng)運(yùn)行時候適當(dāng)條件下nodejs會占用阻塞;

check(檢測): setImmediate()回調(diào)就在這執(zhí)行;

close callbacks(關(guān)閉回調(diào)): 一些關(guān)閉回調(diào),例如socket.on("close", ...),

在事件循環(huán)的每次運(yùn)行過程中,nodejs會檢測是否有任何待處理的異步I/O或者定時器,沒有的話就徹底清除關(guān)閉.

Timers(定時器)

在定時器設(shè)定了一個閾值之后,被提供的回調(diào)函數(shù)實(shí)際執(zhí)行時間可能不是開發(fā)者想要它被執(zhí)行的時間,定時器回調(diào)會在指定閾值過去后盡可能早的運(yùn)行,然而操作系統(tǒng)調(diào)度或者其他回調(diào)運(yùn)行都可能會導(dǎo)致延遲.
注意: 為了防止輪詢階段持續(xù)時間太長,libuv 會根據(jù)操作系統(tǒng)的不同設(shè)置一個輪詢的上限。(這就是為什么上面會說執(zhí)行該階段隊(duì)列的回調(diào)事件直到隊(duì)列耗盡或者超過最大執(zhí)行限度為止)
(下面會多帶帶詳細(xì)講解定時器的東西)

pending callbacks(等待回調(diào))

這階段會執(zhí)行一些系統(tǒng)操作回調(diào)像TCP錯誤類型,例如當(dāng)一個TCP socket想要連接的時候接收到ECONNREFUSED,一些*nix系統(tǒng)會等待錯誤報文,這會被排在pending callbacks 階段執(zhí)行.

poll(輪詢)

這階段有兩個主要功能:

計(jì)算它應(yīng)該阻塞多長的時間和進(jìn)行輪詢I/O操作;
(原文: Calculating how long it should block and poll for I/O, then,我看到有些人會翻譯成當(dāng) timers 的定時器到期后,執(zhí)行定時器(setTimeout 和 setInterval)的 callback。不知道版本不對還是我翻譯不對味)

處理poll隊(duì)列事件;

當(dāng)事件循環(huán)進(jìn)入poll階段,并且沒有timers調(diào)度,會發(fā)生其中一種情況:

如果poll隊(duì)列不為空,事件循環(huán)會迭代回調(diào)隊(duì)列同步執(zhí)行它們直到隊(duì)列耗盡或者到達(dá)系統(tǒng)限制;

如果poll隊(duì)列為空,一件或者多件情況會發(fā)生:

如果setImmediate()腳本已經(jīng)被調(diào)度,事件循環(huán)的poll階段完成然后繼續(xù)到check階段去執(zhí)行那里的調(diào)度腳本;

如果setImmediate()腳本還沒被調(diào)度,事件循環(huán)會等待回調(diào)被添加到隊(duì)列,然后立即執(zhí)行.

一旦poll隊(duì)列清空了事件循環(huán)會檢測有沒有定時器閾值是否到達(dá),如果一個或多個定時器已經(jīng)準(zhǔn)備好,事件循環(huán)會繞回到timers階段去執(zhí)行它們的定時器回調(diào)函數(shù).

check(檢測)

這階段允許開發(fā)者在poll階段完成之后立即執(zhí)行回調(diào)函數(shù),如果poll階段在閑置中并且腳本已經(jīng)被setImmediate()加入隊(duì)列,事件循環(huán)會跳到check階段而不是等待.

setImmediate()實(shí)際上是一個特殊的定時器,它會在事件循環(huán)的多帶帶階段運(yùn)行.通過libuv API在poll階段完成之后調(diào)度回調(diào)去執(zhí)行.

一般來說,當(dāng)代碼執(zhí)行完,事件循環(huán)最終會到達(dá)poll階段去等待即將到來的連接,請求等等.然而,如果一個回調(diào)函數(shù)被setImmediate()調(diào)度并且poll階段是閑置狀態(tài),它會結(jié)束并且跳到check階段而不是在等待輪詢事件.

close callbacks(關(guān)閉回調(diào))

如果一個sockethandle突然被關(guān)閉(例如socket.destroy()),"close"事件會在這階段被觸發(fā),否則會通過process.nextTick()被觸發(fā).

非異步API(強(qiáng)勢插樓)

事件循環(huán)階段部分已經(jīng)講完了,剩下的是定時器之間區(qū)別部分,在那之前我想在這里補(bǔ)充一下定時器知識!

Node.js 中的計(jì)時器函數(shù)實(shí)現(xiàn)使用了一個與瀏覽器類似但不同的內(nèi)部實(shí)現(xiàn),它是基于 Node.js 事件循環(huán)構(gòu)建的。

瀏覽器定時器 setTimeout(callback,delay,lang) :

在指定的毫秒數(shù)后調(diào)用函數(shù)或計(jì)算表達(dá)式,返回一個用于 clearTimeout() 的Timeout或窗口被關(guān)閉。

參數(shù) 描述
callback 必需。要調(diào)用的函數(shù)后要執(zhí)行的 JavaScript 代碼串。
delay 必需。在執(zhí)行代碼前需等待的毫秒數(shù), W3C標(biāo)準(zhǔn)規(guī)定時間間隔低于4ms被算為4ms,具體看瀏覽器
lang 可選。腳本語言可以是:JScript VBScript JavaScript
setInterval(callback,delay,lang) :

按照指定的周期(以毫秒計(jì))來調(diào)用函數(shù)或計(jì)算表達(dá)式。方法會不停地調(diào)用函數(shù),返回一個用于 clearInterval() 的Timeout或窗口被關(guān)閉。
參數(shù)請看上面setTimeout.

nodejs定時器 setTimeout(callback, delay[, ...args])

在指定的毫秒數(shù)后調(diào)用函數(shù)或計(jì)算表達(dá)式,返回一個用于 clearTimeout() 的Timeout或窗口被關(guān)閉。

參數(shù) 描述
callback 必需。要調(diào)用的函數(shù)后要執(zhí)行的 JavaScript 代碼串。
delay 必需。在執(zhí)行代碼前需等待的毫秒數(shù)。當(dāng) delay 大于 2147483647 或小于 1 時,delay 會被設(shè)為 1。
...args 可選, 當(dāng)調(diào)用 callback 時要傳入的可選參數(shù)。

此外還增加一些方法timeout.ref(),timeout.unref()等,請自行查看.Timeout 類

setInterval(callback, delay[, ...args])

按照指定的周期(以毫秒計(jì))來調(diào)用函數(shù)或計(jì)算表達(dá)式。方法會不停地調(diào)用函數(shù),返回一個用于 clearInterval() 的Timeout或窗口被關(guān)閉。
參數(shù)請看上面setTimeout.

setImmediate(callback[, ...args])

預(yù)定立即執(zhí)行的 callback,它是在 I/O 事件的回調(diào)之后被觸發(fā)。 返回一個用于 clearImmediate() 的 Immediate。
當(dāng)多次調(diào)用 setImmediate() 時,callback 函數(shù)會按照它們被創(chuàng)建的順序依次執(zhí)行。 每次事件循環(huán)迭代都會處理整個回調(diào)隊(duì)列。 如果一個即時定時器是被一個正在執(zhí)行的回調(diào)排入隊(duì)列的,則該定時器直到下一次事件循環(huán)迭代才會被觸發(fā)。

參數(shù) 描述
callback 在 Node.js 事件循環(huán)的當(dāng)前回合結(jié)束時要調(diào)用的函數(shù)。
...args 可選, 當(dāng)調(diào)用 callback 時要傳入的可選參數(shù)。

對應(yīng)的清除方法clearImmediate(),此外還增加一些方法setImmediate.ref(),setImmediate.unref()等,請自行查看.Immediate 類

promise寫法(題外話)

可用util.promisify()提供的promises常用變體

const util = require("util");
const setTimeoutPromise = util.promisify(setTimeout),
  setImmediatePromise = util.promisify(setImmediate);

setTimeoutPromise(40, "foobar").then(value => {
  // value === "foobar" (passing values is optional)
  // This is executed after about 40 milliseconds.
});

setImmediatePromise("foobar").then(value => {
  // value === "foobar" (passing values is optional)
  // This is executed after all I/O callbacks.
});

// or with async function
async function timerExample() {
  console.log("Before I/O callbacks");
  await setImmediatePromise();
  console.log("After I/O callbacks");
}
timerExample();
process.nextTick(callback[, ...args])

將 callback 添加到next tick 隊(duì)列。 一旦當(dāng)前事件輪詢隊(duì)列的任務(wù)全部完成,在next tick隊(duì)列中的所有callbacks會被依次調(diào)用。但是不同于上面的定時器.在內(nèi)部的處理機(jī)制不同,nextTick擁有比延時更多的特性.
注意:這不是定時器,而且遞歸調(diào)用nextTick callbacks 會阻塞任何I/O操作,就像一個while(true)循環(huán)一樣

參數(shù) 描述
callback 一旦當(dāng)前事件輪詢隊(duì)列的任務(wù)全部完成,在next tick隊(duì)列中要調(diào)用的函數(shù)
...args 可選, 當(dāng)調(diào)用 callback 時要傳入的可選參數(shù)。
事件輪詢隨后的ticks 調(diào)用,會在任何I/O事件(包括定時器)之前運(yùn)行。
console.log("start");
process.nextTick(() => {
  console.log("nextTick callback");
});
console.log("scheduled");
// Output:
// start
// scheduled
// nextTick callback
在對象構(gòu)造好但還沒有任何I/O發(fā)生之前,想給用戶機(jī)會來指定某些事件處理器。
function MyThing(options) {
  this.setupOptions(options);

  process.nextTick(() => {
    this.startDoingStuff();
  });
}

const thing = new MyThing();
thing.getReadyForStuff();

// thing.startDoingStuff() gets called now, not before.
每次事件輪詢后,在額外的I/O執(zhí)行前,next tick隊(duì)列都會優(yōu)先執(zhí)行。
//使用場景
const maybeTrue = Math.random() > 0.5;

maybeSync(maybeTrue, () => {
  foo();
});

bar();

//危險寫法,因?yàn)椴磺宄oo() 或 bar() 哪個會被先調(diào)用
function maybeSync(arg, cb) {
  if (arg) {
    cb();
    return;
  }

  fs.stat("file", cb);
}

//優(yōu)化寫法,每次事件輪詢后,在額外的I/O執(zhí)行前,next tick隊(duì)列都會優(yōu)先執(zhí)行
function definitelyAsync(arg, cb) {
  if (arg) {
    process.nextTick(cb);
    return;
  }

  fs.stat("file", cb);
}
繼續(xù)回到文章 setImmediate() vs setTimeout()

setImmediate() vs setTimeout()很相似,但是行為方式的不同取決于他們調(diào)用時機(jī).

setImmediate()被設(shè)計(jì)為在當(dāng)前poll階段完成之后執(zhí)行腳本.

setTimeout()會在消耗一段時間閾值之后調(diào)度一段腳本去運(yùn)行.

定時器被執(zhí)行時候的順序變化取決于它們被調(diào)用時候的上下文,如果都是在主模塊內(nèi)部被調(diào)用會受到進(jìn)程性能的約束(可能被本機(jī)其他應(yīng)用運(yùn)行影響);

例如,如果我們不在I/O循環(huán)運(yùn)行下面的腳本(也就是在主模塊中),兩個定時器的執(zhí)行順序是不確定的,因?yàn)樗鼈兪艿竭M(jìn)程性能的約束.

// timeout_vs_immediate.js
setTimeout(function timeout () {
  console.log("timeout");
},0);

setImmediate(function immediate () {
  console.log("immediate");
});
$ node timeout_vs_immediate.js
timeout
immediate

$ node timeout_vs_immediate.js
immediate
timeout

可是如果你把兩個代碼放進(jìn)I/O循環(huán)內(nèi)部,immediate()回調(diào)函數(shù)總是先執(zhí)行;

// timeout_vs_immediate.js
const fs = require("fs");

fs.readFile(__filename, () => {
  setTimeout(() => {
    console.log("timeout");
  }, 0);
  setImmediate(() => {
    console.log("immediate");
  });
});
$ node timeout_vs_immediate.js
immediate
timeout

$ node timeout_vs_immediate.js
immediate
timeout

使用setImmediate()而不是setTimeout()的主要優(yōu)勢是如果在I/O循環(huán)內(nèi)部調(diào)用,setImmediate()總會在所有定時器之前執(zhí)行,與你定義多少個定時器無關(guān).

理解process.nextTick()

你可能已經(jīng)注意到process.nextTick()并沒有出現(xiàn)在圖表,雖然它是異步API的一部分,那是因?yàn)閜rocess.nextTick()技術(shù)上不是事件循環(huán)部分.相反,process.nextTick()會在當(dāng)前操作完成之后被處理,不管事件循環(huán)的當(dāng)前階段如何.

回顧我們的圖表,在給定階段的任何時候你調(diào)用process.nextTick(),傳遞給process.nextTick()的回調(diào)函數(shù)都會在事件循環(huán)繼續(xù)之前被解決,這會造成一些糟糕情況因?yàn)樗试S你通過執(zhí)行遞歸process.nextTick()調(diào)用去"餓死"(starve)你的I/O,從而阻止事件循環(huán)到達(dá)poll階段.

為什么會被允許?

為什么一些像這樣的內(nèi)容會被包含在Nodejs?這部分是因?yàn)樗且环N設(shè)計(jì)哲學(xué),API應(yīng)該總是異步即使它并不需要,看這段代碼片段例子

function apiCall(arg, callback) {
  if (typeof arg !== "string")
    return process.nextTick(callback, new TypeError("argument should be string"));
}

這片段會檢查入?yún)?如果不正確會傳遞錯誤到回調(diào)函數(shù),最近更新的API允許傳遞入?yún)⒌絧rocess.nextTick(),允許他在回調(diào)后取傳遞的任何參數(shù)作為回調(diào)的入?yún)?這樣就不必嵌套函數(shù)了.

這句又長又繞口,附上部分原文:
The API updated fairly recently to allow passing arguments to process.nextTick() allowing it to take any arguments passed after the callback to be propagated as the arguments to the callback so you don"t have to nest functions.

我們要做的是傳遞一個錯誤給開發(fā)者但僅僅是我們已經(jīng)允許開發(fā)者其余的代碼執(zhí)行之后.通過使用process.nextTick()我們保證apiCall()總會在開發(fā)者其余代碼執(zhí)行之后事件循環(huán)允許執(zhí)行之前運(yùn)行它的回調(diào)函數(shù),為了實(shí)現(xiàn)這一步,JS調(diào)用堆棧允許展開立即執(zhí)行所提供的回調(diào)函數(shù),允許開發(fā)者執(zhí)行遞歸調(diào)用process.nextTick()而不會達(dá)到引用錯誤: Maximum call stack size exceeded from v8.

這句又長又繞口,附上原文:
What we"re doing is passing an error back to the user but only after we have allowed the rest of the user"s code to execute. By using process.nextTick() we guarantee that apiCall() always runs its callback after the rest of the user"s code and before the event loop is allowed to proceed. To achieve this, the JS call stack is allowed to unwind then immediately execute the provided callback which allows a person to make recursive calls to process.nextTick() without reaching a RangeError: Maximum call stack size exceeded from v8.

這種哲學(xué)會導(dǎo)致一些潛在的有問題的情況,看看這段片段例子

let bar;

// this has an asynchronous signature, but calls callback synchronously
function someAsyncApiCall(callback) { callback(); }

// the callback is called before `someAsyncApiCall` completes.
someAsyncApiCall(() => {
  // since someAsyncApiCall has completed, bar hasn"t been assigned any value
  console.log("bar", bar); // undefined
});

bar = 1;

開發(fā)者定義someAsyncApiCall()有一個異步簽名(signature??),實(shí)際上卻是同步操作,當(dāng)它調(diào)用時候提供給someAsyncApiCall()的回調(diào)函數(shù)會在事件循環(huán)的相同階段被調(diào)用因?yàn)閟omeAsyncApiCall()實(shí)際上并沒有做任何異步事情,結(jié)果回調(diào)函數(shù)試著去引用bar即使它可能還沒在作用域里,因?yàn)榇a不可能運(yùn)行完成.

但是如果把它放進(jìn)process.nextTick(),代碼依舊有能力跑完,允許所有變量,函數(shù)等等在回調(diào)函數(shù)被調(diào)用之前優(yōu)先初始化完,它具有不讓事件循環(huán)繼續(xù)的優(yōu)點(diǎn),在允許事件循環(huán)繼續(xù)之前,提醒用戶注意錯誤可能是有用的。

這句又長又繞口,附上原文:
The user defines someAsyncApiCall() to have an asynchronous signature, but it actually operates synchronously. When it is called, the callback provided to someAsyncApiCall() is called in the same phase of the event loop because someAsyncApiCall() doesn"t actually do anything asynchronously. As a result, the callback tries to reference bar even though it may not have that variable in scope yet, because the script has not been able to run to completion.

By placing the callback in a process.nextTick(), the script still has the ability to run to completion, allowing all the variables, functions, etc., to be initialized prior to the callback being called. It also has the advantage of not allowing the event loop to continue. It may be useful for the user to be alerted to an error before the event loop is allowed to continue. Here is the previous example using process.nextTick():

這是上面使用process.nextTick()的例子

let bar;

function someAsyncApiCall(callback) {
  process.nextTick(callback);
}

someAsyncApiCall(() => {
  console.log("bar", bar); // 1
});

bar = 1;

這是另一個現(xiàn)實(shí)世界的例子:

const server = net.createServer(() => {}).listen(8080);

server.on("listening", () => {});

(這句又長又繞口,不想翻了:)
When only a port is passed, the port is bound immediately. So, the "listening" callback could be called immediately. The problem is that the .on("listening") callback will not have been set by that time.

想要避開這問題,"listening"事件會加入nextTick()隊(duì)列以容許腳本運(yùn)行完,這允許開發(fā)者設(shè)置任何他們想要的任何事件處理器.

process.nextTick() vs setImmediate()

就用戶而言,我們有兩個類似的調(diào)用,不過他們的名字令人困惑.
process.nextTick() 在同一階段立刻觸發(fā)(原文fires: 點(diǎn)燃;解雇;開除;使發(fā)光;燒制;激動;放槍???)
setImmediate() 在事件循環(huán)的后續(xù)迭代或“tick”中觸發(fā)(原文fires)

本質(zhì)上,名字應(yīng)該調(diào)換,process.nextTick()比setImmediate()更加容易觸發(fā),但這是一種不可變得的過去的產(chǎn)物,這種轉(zhuǎn)換會在npm中破壞大量的包,每天都有很多新包被添加,意味著我們每等待一天就有更多潛在的破壞發(fā)生,即使它們多困惑也不能更改它們的名字.

我們建議開發(fā)者們在任何情況使用setImmediate()因?yàn)樗菀淄瞥?reason about??)(它會讓代碼兼容更廣泛的環(huán)境變量,像browser JS)

為什么使用process.nextTick()?(翻譯文章最后內(nèi)容)

兩個原因:
1, 允許開發(fā)者們處理錯誤,清除任何不需要的資源,或者嘗試在事件循環(huán)繼續(xù)之前再次發(fā)起請求.
2, 在需要的時候允許調(diào)用棧釋放(unwound??)之后但事件循環(huán)繼續(xù)之前運(yùn)行一個回調(diào)函數(shù).

一個符合開發(fā)者們期望的簡單例子

const server = net.createServer();
server.on("connection", (conn) => { });

server.listen(8080);
server.on("listening", () => { });

假設(shè)listen()在事件循環(huán)開始的時候運(yùn)行,但是監(jiān)聽回調(diào)被放置在setImmediate()。除非傳遞主機(jī)名立即綁定端口,想讓事件循環(huán)繼續(xù)進(jìn)行必須進(jìn)入poll階段,意味著有機(jī)會(a non-zero chance??)已經(jīng)接收到一個連接,允許在監(jiān)聽事件之前觸發(fā)連接事件。

(有段名詞不懂怎么翻譯:)
which means there is a non-zero chance that a connection could have been received allowing the connection event to be fired before the listening event

另一個例子是運(yùn)行構(gòu)造函數(shù),從EventEmitter繼承并且想要在構(gòu)造函數(shù)內(nèi)部調(diào)用一個事件。

const EventEmitter = require("events");
const util = require("util");

function MyEmitter() {
  EventEmitter.call(this);
  this.emit("event");
}
util.inherits(MyEmitter, EventEmitter);

const myEmitter = new MyEmitter();
myEmitter.on("event", () => {
  console.log("an event occurred!");
});

我們不能在構(gòu)造函數(shù)立刻發(fā)出事件是因?yàn)槟_本可能還沒處理到開發(fā)者設(shè)置觸發(fā)事件回調(diào)函數(shù)的位置,所以在構(gòu)造函數(shù)內(nèi)部本身你能使用process.nextTick()設(shè)置觸發(fā)事件回調(diào)函數(shù)以在構(gòu)造函數(shù)已經(jīng)完成之后提供期望結(jié)果。

const EventEmitter = require("events");
const util = require("util");

function MyEmitter() {
  EventEmitter.call(this);

  // use nextTick to emit the event once a handler is assigned
  process.nextTick(() => {
    this.emit("event");
  });
}
util.inherits(MyEmitter, EventEmitter);

const myEmitter = new MyEmitter();
myEmitter.on("event", () => {
  console.log("an event occurred!");
});
輸出例子

你們試試看這個輸出順序符不符合你們預(yù)期

const fs = require("fs");

console.log("start");
setTimeout(function timeout() {
  console.log("模塊外部timeout");
}, 1000);

setImmediate(function immediate() {
  console.log("模塊外部immediate");
});

process.nextTick(() => {
  console.log("模塊外部nextTick callback");
});

fs.readFile(__filename, () => {
  setTimeout(function timeout() {
    console.log("I/O內(nèi)部timeout");
  }, 0);

  setImmediate(function immediate() {
    console.log("I/O內(nèi)部immediate");
  });

  process.nextTick(() => {
    console.log("I/O內(nèi)部nextTick callback");
  });
});

console.log("end");
// start
// end
// 模塊外部nextTick callback
// 模塊外部immediate
// I/O內(nèi)部nextTick callback
// I/O內(nèi)部immediate
// I/O內(nèi)部timeout
// 模塊外部timeout
Nodejs劣勢

總的來說單線程的鍋.
1, 異常拋出終止
我們都知道Javascript是一門單線程語言,在發(fā)生各種錯誤之后,JavaScript引擎通常會停止,并拋出一個錯誤.
Nodejs具體錯誤直接看Error (錯誤).
暫時還沒研究到,但是肯定可以通過一些方法解決的,后補(bǔ).

2, 不適合CPU密集型
盡管我們上面已經(jīng)提出了事件驅(qū)動異步IO非阻塞模型的各種優(yōu)點(diǎn),但是里面有個關(guān)鍵詞叫"I/O",如果是非I/O的處理例如CPU計(jì)算還是沒改進(jìn)的,如果有長時間運(yùn)行的計(jì)算,將會導(dǎo)致CPU時間片不能釋放,使得后續(xù)I/O無法發(fā)起.
可以通過把密集運(yùn)算拆分成多個小任務(wù),減輕CPU壓力.

3, 不能用到CPU的多核
現(xiàn)在的服務(wù)器操作系統(tǒng)基本都是支持多CPU/核了,單線程言語注定只能占用一個資源,不能充分利用.

解決單線程痛點(diǎn)方案
可以新開進(jìn)程去玩,還沒研究到不說.
process - 進(jìn)程

參考資源

Node.js 中文網(wǎng) API
The Node.js Event Loop, Timers, and process.nextTick()

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/106704.html

相關(guān)文章

  • Nodejs性能原理(上) --- 異步非阻塞事件驅(qū)動模型

    摘要:使用了一個事件驅(qū)動非阻塞式的模型,使其輕量又高效。的包管理器,是全球最大的開源庫生態(tài)系統(tǒng)。按照這個定義,之前所述的阻塞,非阻塞,多路復(fù)用信號驅(qū)動都屬于同步。 系列文章 Nodejs高性能原理(上) --- 異步非阻塞事件驅(qū)動模型Nodejs高性能原理(下) --- 事件循環(huán)詳解 前言 終于開始我nodejs的博客生涯了,先從基本的原理講起.以前寫過一篇瀏覽器執(zhí)行機(jī)制的文章,和nodej...

    yy736044583 評論0 收藏0
  • 優(yōu)秀文章收藏(慢慢消化)持續(xù)更新~

    摘要:整理收藏一些優(yōu)秀的文章及大佬博客留著慢慢學(xué)習(xí)原文協(xié)作規(guī)范中文技術(shù)文檔協(xié)作規(guī)范阮一峰編程風(fēng)格凹凸實(shí)驗(yàn)室前端代碼規(guī)范風(fēng)格指南這一次,徹底弄懂執(zhí)行機(jī)制一次弄懂徹底解決此類面試問題瀏覽器與的事件循環(huán)有何區(qū)別筆試題事件循環(huán)機(jī)制異步編程理解的異步 better-learning 整理收藏一些優(yōu)秀的文章及大佬博客留著慢慢學(xué)習(xí) 原文:https://www.ahwgs.cn/youxiuwenzhan...

    JeOam 評論0 收藏0
  • JS代碼在nodejs環(huán)境執(zhí)行機(jī)制和事件循環(huán)

    摘要:開始執(zhí)行文件,同步代碼執(zhí)行完畢后,進(jìn)入事件循環(huán)。時間未到的時候,如果有事件返回,就執(zhí)行該事件注冊的回調(diào)函數(shù)。對于多次執(zhí)行輸出結(jié)果不同,需要了解事件循環(huán)的基礎(chǔ)問題。 1. 說明 nodejs是單線程執(zhí)行的,同時它又是基于事件驅(qū)動的非阻塞IO編程模型。這就使得我們不用等待異步操作結(jié)果返回,就可以繼續(xù)往下執(zhí)行代碼。當(dāng)異步事件觸發(fā)之后,就會通知主線程,主線程執(zhí)行相應(yīng)事件的回調(diào)。 本篇文章講解n...

    Lowky 評論0 收藏0
  • JavaScript單線程事件循環(huán)(Event Loop)那些事

    摘要:概述本篇主要介紹的運(yùn)行機(jī)制單線程事件循環(huán)結(jié)論先在中利用運(yùn)行至完成和非阻塞完成單線程下異步任務(wù)的處理就是先處理主模塊主線程上的同步任務(wù)再處理異步任務(wù)異步任務(wù)使用事件循環(huán)機(jī)制完成調(diào)度涉及的內(nèi)容有單線程事件循環(huán)同步執(zhí)行異步執(zhí)行定時器的事件循環(huán)開始 1.概述 本篇主要介紹JavaScript的運(yùn)行機(jī)制:單線程事件循環(huán)(Event Loop). 結(jié)論先: 在JavaScript中, 利用運(yùn)行至...

    Shisui 評論0 收藏0
  • JS與Node.js中的事件循環(huán)

    摘要:的單線程,與它的用途有關(guān)。特點(diǎn)的顯著特點(diǎn)異步機(jī)制事件驅(qū)動。隊(duì)列的讀取輪詢線程,事件的消費(fèi)者,的主角。它將不同的任務(wù)分配給不同的線程,形成一個事件循環(huán),以異步的方式將任務(wù)的執(zhí)行結(jié)果返回給引擎。 這兩天跟同事同事討論遇到的一個問題,js中的event loop,引出了chrome與node中運(yùn)行具有setTimeout和Promise的程序時候執(zhí)行結(jié)果不一樣的問題,從而引出了Nodejs的...

    abson 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<