中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

nodejs中的事件和事件循環是什么意思

發布時間:2020-12-09 11:51:00 來源:億速云 閱讀:418 作者:小新 欄目:web開發

小編給大家分享一下nodejs中的事件和事件循環是什么意思,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

事件

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中的事件和事件循環是什么意思”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

宜城市| 新巴尔虎左旗| 阿勒泰市| 永泰县| 红原县| 天等县| 开封县| 宿松县| 新巴尔虎左旗| 苍南县| 赞皇县| 江孜县| 西峡县| 桑日县| 平和县| 连平县| 肇州县| 五河县| 化州市| 浦县| 夏河县| 搜索| 高尔夫| 通州区| 大庆市| 轮台县| 镇康县| 修文县| 台山市| 汝州市| 房山区| 大姚县| 寿宁县| 文安县| 祥云县| 开原市| 苗栗市| 泗阳县| 曲周县| 青岛市| 雅江县|