1. 程式人生 > >NodeJS研究筆記:非同步程式設計導致難以察覺的bug

NodeJS研究筆記:非同步程式設計導致難以察覺的bug

NodeJS 一顯著特點是它的非同步處理特性。例如,當我讀入一個檔案時,Node會將檔案讀取操作放在後臺處理,當程式提交讀取請求後,不必堵塞在原地等待請求完成,而是提供一個請求完成的回撥函式,然後接著進行其他工作,一旦檔案讀取完成後,我們提供的函式會被及時呼叫,在回撥函式中,再對讀到的檔案內容進行處理。正是由於這種非同步處理機制,使得Node成為後臺開發的一大利器,特別是其非同步處理框架提供的高效率,使得它最近成為web後臺開發的重要選擇。

但是,靈活性是有成本的,非同步處理模式提高了程式設計邏輯的複雜性,一旦稍有粗心大意,非同步流程會導致一些難以察覺的bug, 例如下面的例子:

var EventEmitter = require('events').EventEmitter;

function doSomeThingSlow() {
	var events = new EventEmitter();
	 events.emit('success');
	
	return events;
}

doSomeThingSlow().on('success', function() {
	console.log('success!');
});

在doSomeThingSlow  中,通過EventEmitter向訊息佇列發生了一個事件訊息叫success, 在底部,我們通過emitter 的on函式,監聽success訊息,一旦發現該訊息,我們做相應的處理。但這段程式有bug, 問題就是‘success'訊息的處理函式不會被執行,也就是console.log('success!'); 無法執行。主要問題在於當doSomeThingSlow執行時,在events.emit將success事件觸發時,程式還沒有執行到底部的doSomeThingSlow.on(...), 也就是success事件觸發時,我們來不及為該事件註冊對應的處理函式。但程式執行到底部的on部分時,才開始對'success'事件註冊處理函式,但此時,事件已經觸發完畢,因此也就錯過了事件的處理機會。

解決該問題的方法是,利用 Node的非同步處理機制,將events.emit('success') 這一觸發動作放入到訊息佇列中非同步執行:

var EventEmitter = require('events').EventEmitter;

function doSomeThingSlow() {
	var events = new EventEmitter();
	process.nextTick(function() {
		events.emit('success');
	});
	 
	
	return events;
}

doSomeThingSlow().on('success', function() {
	console.log('success!');
});

process.nextTick 會將給定的函式新增到loop迴圈的下一個執行佇列中,也就是說,nextTick 會將傳給它的函式新增到主迴圈的待處理佇列中等待處理,然後立刻返回,這樣on內部的程式碼會先於events.emit被執行,也就是在事件觸發前,事件的處理函式會先被註冊,這樣當events.emit('success')執行時,console.log('success!')就會被執行。

所以,如果執行上一個程式,我們在控制檯看不到列印資訊,執行第二個程式,我們才能看到列印資訊。此類bug比較詭異,如果對Node的非同步處理機制理解不深的話,就很難發現這種問題。

相關推薦

NodeJS研究筆記非同步程式設計導致難以察覺bug

NodeJS 一顯著特點是它的非同步處理特性。例如,當我讀入一個檔案時,Node會將檔案讀取操作放在後臺處理,當程式提交讀取請求後,不必堵塞在原地等待請求完成,而是提供一個請求完成的回撥函式,然後接著進行其他工作,一旦檔案讀取完成後,我們提供的函式會被及時呼叫,在回撥函式中

effectiveJava學習筆記通用程式設計(一)

將區域性變數的作用域最小化 其實大部分人還是在第一次使用變數的時候宣告變數的,在開頭就將所有變數宣告的還沒見過。 要使區域性變數的作用域最小化,最有力的方法就是在第一次使用它的地方宣告。 但是,這裡書中講到了for迴圈優於while迴圈,值得我們注意。 for迴圈將變數宣告在迴圈內,

effectiveJava學習筆記通用程式設計(二)

當心字串連線的效能 由於String是final的,不可變,他內部每次拼接都會建立一個StringBuffer物件,這樣你如果拼接n次,那麼他建立了n次物件,效能低下,而StringBuilder只在外面建立了一個物件,其他直接append字串即可。 所以,我們在迴圈中拼接字串的時候要尤其注

python學習筆記python程式設計基礎

1、python的包的種類 自帶的包(built-in)package和外部(external)package 1.1 自帶package,舉例os、os.getwd().   3、環境變數中配置easy_install,pip 沒有提供引數,需要設定引數 easy

Python並行程式設計(十四)非同步程式設計

1、基本概念   除了順序執行和並行執行的模型以外,還有非同步模型,這是事件驅動模型的基礎。非同步活動的執行模型可以只有一個單一的主控制流,能在單核心繫統和多核心繫統中執行。   在併發執行的非同步模型中,許多工被穿插在同一時間線上,所有的任務都由一個控制流執行(單一執行緒)。任務的執行可能被暫停或恢復,中間

C#基礎系列非同步程式設計初探async和await

前言:前面有篇從應用層面上面介紹了下多執行緒的幾種用法,有博友就說到了async, await等新語法。確實,沒有非同步的多執行緒是單調的、乏味的,async和await是出現在C#5.0之後,它的出現給了非同步並行變成帶來了很大的方便。非同步程式設計涉及到的東西還

TensorFlow學習筆記檔名衝突導致報錯

教材:《TensorFlow實戰:Google深度學習框架》章節:7.3.1 佇列與多執行緒學習使用TensorFlow中的佇列,程式碼非常簡單:import tensorflow as tf # 建立一個先進先出佇列,指定佇列中最多可以儲存兩個元素,並指定型別為整數 q

java工作筆記web 程式設計中關於jni和jna兩種工具操作和效能對比測試

       第一次發部落格有點緊張哈。        最近剛剛公司轉崗從底層C語言的編寫到做Java的web restful架構。其中需要呼叫底層C++程式碼庫。所以對於選擇哪種方法從Java呼叫C的程式碼做了簡單地學習和對比測試。在這裡把他們貼出了。希望能有大神出來指點

NodeJS研究筆記,利用目錄來實現跨平臺檔案鎖

檔案加鎖是多執行緒或多程序開發時,確保檔案資源不被併發讀寫所汙染的有效措施,NodeJS提供了一些辦法確保在競爭性環境下,檔案資料的一致性。 NodeJS保證檔案讀取一致性的辦法有兩種,一是讀取檔案時設定隔離標誌,隔離標誌是指,當使用fs模組開啟檔案時,在開啟

基礎篇非同步程式設計不會?我教你啊!CompeletableFuture

# 前言 以前需要非同步執行一個任務時,一般是用Thread或者執行緒池Executor去建立。如果需要返回值,則是呼叫Executor.submit獲取Future。但是多個執行緒存在依賴組合,我們又能怎麼辦?可使用同步元件CountDownLatch、CyclicBarrier等;其實有簡單的方法,就是用

nodejs學習筆記閉包和非同步程式設計

閉包到底是什麼鬼 閉包就是函式��,但是它可以繼承並訪問它自身被宣告的那個作用域裡的變數。當你將一個回撥函式作為引數傳遞給另外一個進行i/o操作的函式時,回撥函式稍後會被呼叫,神奇的是,在被呼叫時,回撥函式會記住它自身宣告時所在的上下文,並且可以訪問該上下文及

《Java8實戰》-第十一章筆記(CompletableFuture組合式非同步程式設計

CompletableFuture:組合式非同步程式設計 最近這些年,兩種趨勢不斷地推動我們反思我們設計軟體的方式。第一種趨勢和應用執行的硬體平臺相關,第二種趨勢與應用程式的架構相關,尤其是它們之間如何互動。我們在第7章中已經討論過硬體平臺的影響。我們注意到隨著多核處理器的出現,提升應用程式處理速度最有效的

nodejs學習筆記什麼是事件驅動程式設計風格

這年頭nodeJS真是如日中天啊,學前端的要是不知道nodeJS,都不好意思說自己和懂點程式碼的美工有啥區別,談到nodeJS就不得不先聊聊它的事件驅動程式設計風格。究竟什麼是事件驅動程式設計風格呢? 在傳統程式設計中,i/o操作和本地函式呼叫的處理方式相同:

深入淺出nodejs學習筆記——非同步程式設計

高階函式:與傳統函式只能傳遞或返回規定的資料型別相比,高階函式可以返回函式,如下: function foo(x){       return function(){            return x; } } 由於高階函式可以傳遞函式為引數,因此可以傳遞不同型別的函

NodeJS學習筆記 進階 (11)Nodejs 進階調試日誌打印debug模塊

-c clas a* deb urn uid 0.11 log 打印 前言 在node程序開發中時,經常需要打印調試日誌。用的比較多的是debug模塊,比如express框架中就用到了。下文簡單舉幾個例子進行說明。文中相關代碼示例,可在這裏找到。 備註:node在0.11

《CLR Via C#》讀書筆記27.計算限制的非同步操作

一、CLR 執行緒池基礎 一般來說如果計算機的 CPU 利用率沒有 100% ,那麼說明很多程序的部分執行緒沒有執行。可能在等待 檔案/網路/資料庫等裝置讀取或者寫入資料,又可能是等待按鍵、滑鼠移動等事件。 執行 I/O 限制的操作時,作業系統通過裝置驅動程式通知硬體幹活,而 CPU 處於一種空閒狀態。而

JavaScript非同步程式設計筆記

非同步事件的工作方式 事件!事件到底是怎麼工作的?JavaScript出現了多久,對JavaScript非同步事件模型就迷惘了多久。迷惘導致bug,bug導致加班,加班導致沒時間撩妹子,這不是js攻城獅想要的生活。 ==為了妹子,一定要理解好JavaScript事件== JavaScript事件的執行

讀書筆記「Python程式設計從入門到實踐」_7.使用者輸入和while迴圈

7.1 函式input()的工作原理   函式input() 讓程式暫停執行,等待使用者輸入一些文字。獲取使用者輸入後,Python將其儲存在一個變數中,以方便你使用。   message = input("Tell me something, and I will repeat it back t

C++ 快速入門筆記進階程式設計

C++入門筆記:高階程式設計 檔案和流 開啟檔案 void open (const char *filename, ios::openmode mode); ios::app 追加模式。所有寫入都追加到檔案末尾 ios::ate 檔案開啟後定位到檔案末尾

C++ 快速入門筆記面向物件程式設計

類 & 物件 類定義 class Box { public: double length; // Length of a box double breadth; // Breadth of a box double height;