目錄
事件
事件循環
事件循環的阻塞
事件循環範例
堆疊和訊息佇列
作業佇列和promise
process.nextTick()
setImmediate()
setInterval()
首頁 web前端 js教程 了解nodejs中的事件和事件循環

了解nodejs中的事件和事件循環

Dec 08, 2020 pm 05:40 PM
node.js 事件 事件監聽 事件驅動 非同步程式設計

了解nodejs中的事件和事件循環

相關推薦:《nodejs 教學

#熟悉javascript的朋友應該都使用過事件,例如滑鼠的移動,滑鼠的點擊,鍵盤的輸入等等。我們在javascript中監聽這些事件,從而觸發對應的處理。

同樣的nodejs中也有事件,而且還有一個專門的events模組來進行專門的處理。

同時事件和事件循環也是nodejs建構非同步IO的非常重要的概念。

今天我們來詳細了解一下。

事件

nodejs為事件提供了一個專門的模組:lib/events.js。

還記得我們在講使用nodejs建立web伺服器嗎?

const server = http.createServer((req, res) => {
  res.statusCode = 200
  res.setHeader('Content-Type', 'text/plain')
  res.end('welcome to www.flydean.com\n')
})
登入後複製

這裡,每個請求都會觸發request事件。

nodejs的核心API是基於非同步事件驅動程式來進行架構的,所以nodejs中有非常多的事件。

例如:net.Server 會在每次有新連線時觸發事件,fs.ReadStream 會在開啟檔案時觸發事件,stream會在資料可讀時觸發事件。

我們來看怎麼建構一個nodejs的事件:

const EventEmitter = require('events')
const eventEmitter = new EventEmitter()
登入後複製

events常用的方法有兩個,分別是on和emit。

on用來監聽事件,emit用來觸發事件。

eventEmitter.on('fire', () => {
  console.log('开火')
})

eventEmitter.emit('fire')
登入後複製

emit也可以帶參數,我們看下一個參數的情況:

eventEmitter.on('fire', who => {
  console.log(`开火 ${who}`)
})

eventEmitter.emit('fire', '美帝')
登入後複製

再看看兩個參數的情況:

eventEmitter.on('fire', (who, when) => {
  console.log(`开火 ${who} ${when}`)
})

eventEmitter.emit('fire', '川建国','now')
登入後複製

預設情況下,EventEmitter以註冊的順序同步地呼叫所有監聽器。這樣可以確保事件的正確排序,並有助於避免競態條件和邏輯錯誤。

如果需要非同步執行,則可以使用setImmediate() 或 process.nextTick()來切換到非同步執行模式。

eventEmitter.on('fire', (who, when) => {
    setImmediate(() => {
      console.log(`开火 ${who} ${when}`);
  });
})

eventEmitter.emit('fire', '川建国','now')
登入後複製

除此之外,events還支援其他幾個方法:

once(): 新增單次監聽器

removeListener() / off(): 從事件中移除事件監聽器

removeAllListeners(): 移除事件的所有監聽器

事件循環

我們知道nodejs的程式碼是運行在單執行緒環境中的,每次只會去處理一件事情。

這一種處理方式,避免了多執行緒環境的資料同步的問題,大大的提升了處理效率。

所謂事件循環,就是指處理器在一個程式週期中,處理完這個週期的事件之後,會進入下一個事件週期,處理下一個事件週期的事情,這樣一個週期一個週期的循環。

事件循環的阻塞

如果我們在事件處理過程中,某個事件的處理髮生了阻塞,則會影響其他的事件的執行,所以我們可以看到在JS中,幾乎所有的IO都是非阻塞的。這也是為什麼javascript有這麼多回呼的原因。

事件循環範例

我們看一個簡單的事件循環的例子:

const action2 = () => console.log('action2')

const action3 = () => console.log('action3')

const action1 = () => {
    console.log('action1')
    action2()
    action3()
}

action1()
登入後複製

上面的程式碼輸出:

action1
action2
action3
登入後複製

堆疊和訊息佇列

我們知道函數間的呼叫是透過堆疊來實現的,上面的例子中,我們的呼叫順序也是透過堆疊來實現的。

但不是函數中所有的方法都會入棧,還有一些方法會被放入訊息佇列。

我們再舉一個例子:

const action2 = () => console.log('action2')

const action3 = () => console.log('action3')

const action1 = () => {
    console.log('action1')
    setTimeout(action2, 0)
    action3()
}

action1()
登入後複製

上面的程式碼運行結果:

action1
action3
action2
登入後複製

結果不一樣了。這是因為settimeout觸發了定時器,當定時器到期的時候,回呼函數會被放入訊息佇列中等待被處理,而不是放入堆疊中。

事件循環會優先處理堆疊中的事件,只有堆疊中沒有任何資料的時候,才會去轉而消費訊息佇列中的事件。

雖然上面範例中setTimeout的timeout時間是0,但還是要等到action3執行完畢才能執行。

注意,setTimeout中的timeout並不是在目前的執行緒進行等待的,它是由瀏覽器或其他JS執行環境來呼叫。

作業佇列和promise

ES6中的Promise引入了作業佇列的概念,使用作業佇列將會盡快執行非同步函數的結果,而不是放在呼叫堆疊的末端。

舉例:

const action2 = () => console.log('action2')

const action3 = () => console.log('action3')

const action1 = () => {
    console.log('action1')
    setTimeout(action2, 0)
    new Promise((resolve, reject) =>
        resolve('应该在action3之后、action2之前')
    ).then(resolve => console.log(resolve))
    action3()
}

action1()
登入後複製

輸出結果:

action1
action3
应该在action3之后、action2之前
action2
登入後複製

這是因為,在目前函數結束之前 resolve 的 Promise 會在目前函數之後立即執行。

也就是說先執行堆疊,再執行作業佇列,最後執行訊息佇列。

process.nextTick()

先給大家一個定義叫做tick,一個tick就是指一個事件週期。而process.nextTick()就是指在下一個事件循環tick開始之前,呼叫這個函數:

process.nextTick(() => {
  console.log('i am the next tick');
})
登入後複製

所以nextTick一定要比訊息佇列的setTimeout快。

setImmediate()

nodejs提供了一個setImmediate方法,來盡快的執行程式碼。

setImmediate(() => {
  console.log('I am immediate!');
})
登入後複製

setImmediate中的函數會在事件循環的下一個迭代中執行。

setImmediate() 和 setTimeout(() => {}, 0)的功能基本上是類似的。它們都會在事件循環的下一個迭代中運行。

setInterval()

如果想要定時執行某些回呼函數,則需要使用到setInterval。

setInterval(() => {
  console.log('每隔2秒执行一次');
}, 2000)
登入後複製

要清除上面的定时任务,可以使用clearInterval:

const id = setInterval(() => {
  console.log('每隔2秒执行一次');
}, 2000)

clearInterval(id)
登入後複製

注意,setInterval是每隔n毫秒启动一个函数,不管该函数是否执行完毕。

如果一个函数执行时间太长,就会导致下一个函数同时执行的情况,怎么解决这个问题呢?

我们可以考虑在回调函数内部再次调用setTimeout,这样形成递归的setTimeout调用:

const myFunction = () => {
  console.log('做完后,隔2s再次执行!');

  setTimeout(myFunction, 2000)
}

setTimeout(myFunction, 2000)
登入後複製

更多编程相关知识,请访问:编程视频!!

以上是了解nodejs中的事件和事件循環的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
4 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何用 C++ 函數實作非同步程式設計? 如何用 C++ 函數實作非同步程式設計? Apr 27, 2024 pm 09:09 PM

摘要:C++中的非同步程式設計允許多工處理,無需等待耗時操作。使用函數指標建立指向函數的指標。回調函數在非同步操作完成時被呼叫。 boost::asio等函式庫提供非同步程式支援。實戰案例示範如何使用函數指標和boost::asio實現非同步網路請求。

C++ 函式在並發程式設計中的事件驅動機制? C++ 函式在並發程式設計中的事件驅動機制? Apr 26, 2024 pm 02:15 PM

並發程式設計中的事件驅動機制透過在事件發生時執行回呼函數來回應外部事件。在C++中,事件驅動機制可用函數指標實作:函數指標可以註冊回呼函數,在事件發生時執行。 lambda表達式也可以實現事件回調,允許建立匿名函數物件。實戰案例使用函數指標實作GUI按鈕點擊事件,在事件發生時呼叫回呼函數並列印訊息。

Java框架非同步程式設計中常見的問題與解決方案 Java框架非同步程式設計中常見的問題與解決方案 Jun 04, 2024 pm 05:09 PM

Java框架非同步程式設計中常見的3個問題和解決方案:回呼地獄:使用Promise或CompletableFuture以更直覺的風格管理回呼。資源競爭:使用同步原語(如鎖)保護共享資源,並考慮使用執行緒安全性集合(如ConcurrentHashMap)。未處理異常:明確處理任務中的異常,並使用異常處理框架(如CompletableFuture.exceptionally())處理異常。

golang框架如何處理並發和非同步程式設計? golang框架如何處理並發和非同步程式設計? Jun 02, 2024 pm 07:49 PM

Go框架利用Go的並發和非同步特性提供高效處理並發和非同步任務的機制:1.透過Goroutine實現並發,允許同時執行多個任務;2.透過通道實現非同步編程,在不阻塞主執行緒的情況下執行任務;3.適用於實戰場景,如並發處理HTTP請求、非同步取得資料庫資料等。

jQuery中如何實作select元素的改變事件綁定 jQuery中如何實作select元素的改變事件綁定 Feb 23, 2024 pm 01:12 PM

jQuery是一個受歡迎的JavaScript函式庫,可以用來簡化DOM操作、事件處理、動畫效果等。在web開發中,常常會遇到需要對select元素進行改變事件綁定的情況。本文將介紹如何使用jQuery實作對select元素改變事件的綁定,並提供具體的程式碼範例。首先,我們需要使用標籤來建立一個包含選項的下拉式選單:

PHP 非同步程式設計的優勢與劣勢? PHP 非同步程式設計的優勢與劣勢? May 06, 2024 pm 10:00 PM

非同步程式設計在PHP的優勢包括更高的吞吐量、更低的延遲、更好的資源利用和可擴展性。其劣勢包括複雜性、調試難度和有限的庫支援。在實戰案例中,ReactPHP用於處理WebSocket連接,展示了非同步程式設計的實際應用。

Python非同步程式設計: 實現高效並發的非同步程式碼之道 Python非同步程式設計: 實現高效並發的非同步程式碼之道 Feb 26, 2024 am 10:00 AM

1.為什麼要使用非同步程式設計?傳統程式設計使用阻塞式I/O,這表示程式會等待某個操作完成,然後才能繼續執行。這對於處理單一任務可能很有效,但對於處理大量任務時,可能會導致程式變慢。非同步程式設計則打破了傳統阻塞式I/O的限制,它使用非阻塞式I/O,這意味著程式可以將任務分發到不同的執行緒或事件循環中執行,而無需等待任務完成。這允許程式同時處理多個任務,提高程式的效能和效率。 2.python非同步程式設計的基礎Python非同步程式設計的基礎是協程和事件循環。協程是允許函數在暫停和恢復之間切換的函數。事件循環則負責調度

如何使用 PHP 建立基於事件的應用程式 如何使用 PHP 建立基於事件的應用程式 May 04, 2024 pm 02:24 PM

在PHP中建構基於事件的應用程式的方法包括:使用EventSourceAPI建立事件來源,並在客戶端使用EventSource物件監聽事件。使用伺服器傳送的事件(SSE)傳送事件,並在客戶端使用XMLHttpRequest物件監聽事件。一個實用的例子是在電子商務網站中使用EventSource即時更新庫存計數,在伺服器端透過隨機更改庫存並發送更新來實現,客戶端則透過EventSource監聽庫存更新並即時顯示。

See all articles