詳談nodejs非同步程式設計_node.js
目前需求涉及大量的非同步操作,實際的頁面越來越傾向於單一頁面應用程式。以後可以使用backbone、angular、knockout等框架,但是關於非同步程式設計的問題是首先需要面對的問題。隨著node的興起,非同步程式設計成為一個非常熱門的話題。經過一段時間的學習和實踐,對非同步程式設計的一些細節進行總結。
1.非同步程式設計的分類
解決非同步問題方法大致包含:直接回呼、pub/sub模式(事件模式)、非同步程式庫控制庫(例如async、when)、promise、Generator等。
1.1 回呼函數
回呼函數是常用的解決非同步的方法,經常接觸和使用到,易於理解,並且在函式庫或函數中非常容易實現。這種也是大家接使用非同步程式設計常用來的方法。
但是回呼函數的方式有以下的問題:
1. 可能形成萬惡的巢狀金字塔,程式碼不容易閱讀;
2. 只能對應一個回呼函數,在許多場景中成為一個限制。
1.2 pub/sub模式(事件)
此模式又稱為事件模式,是回呼函數的事件化,在jQuery等類別庫中非常常見。
事件發布訂閱者模式本身並無同步與非同步呼叫的問題,但在node中,emit呼叫多半是伴隨事件循環而異步觸發的。此模式常用來解耦業務邏輯,事件發布者無須關注註冊的回呼函數,也不用關注回呼函數的個數,資料通過訊息的方式可以很靈活的傳遞。
此模式的優點是:1. 方便理解;2. 不再侷限於一個回呼函數。
不好的地方時: 1. 需要藉助類別庫;2. 事件與回呼函數的順序很重要
var img = document.querySelect(#id);
img.addEventListener('load', function() {
// 圖片載入完成
......
});
img.addEventListener('error', function() {
// 出問題了
......
});
上述程式碼有兩個問題:
a. img實際上已載入完成,此時才綁定load回呼函數,結果回呼函數,結果回呼不會執行,但仍希望執行此對應回呼函數。
var img = document.querySelect(#id);
function load() {
...
}
if(img.complete) {
load();
} else {
img.addEventListener('load', load);
}
img.addEventListener('error', function() {
// 出問題了
......
});
b. 無法很好處理存在異常
結論:事件機制最適合處理同一個物件上反覆發生的事情,不需要考慮當綁定回呼函數之前事件發生的情況。
1.3 非同步控制庫
目前的非同步資料庫主要有Q、when.js、win.js、RSVP.js等。
這些函式庫的特點是程式碼是線性的,可以從上到下完成書寫,符合自然習慣。
不好的地方也是風格各異,不便於閱讀,增加學習成本。
1.4 Promise
Promise翻譯成中文為承諾,個人理解是非同步完成之後,就會給外在一個結果(成功或失敗),並承諾結果不再改變。換句話說是Promise反應了一個操作的最終回傳結果值(A promise represents the eventual value returned from the single completion of an operation)。目前Promise已經引進到ES6規範裡面,Chrome、firefox等高階瀏覽器已經在內部實作了這個原生方法,使用起來相當方便。
以下由下列幾個面向解析Promise的特性:
1.4.1 狀態
包含三種狀態:pending、fulfilled、rejected,三種狀態只能發生兩種轉換(從pending--->fulfilled、pending—>rejected),且狀態的轉換僅能發生一次。
1.4.2 then方法
then方法用於指定非同步事件完成之後的回呼函數。
這個方法可以說是Promise的靈魂方法,這個方法讓Promise充滿了魔法。有以下幾個具體表現:
a) then方法返回Promise。這樣就實現了多個非同步操作的串行操作。
關於上圖黃圈1的對value的處理是Promise裡面較為複雜的一個地方,value的處理分為兩種情況:Promise物件、非Promise物件。
當value 不是Promise類型時,直接將value作為第二個Promise的resolve的參數值即可;當為Promise類型時,promise2的狀態、參數完全由value決定,可以認為promsie2完全是value的傀儡,promise2只是連接不同非同步的橋樑。
Promise.prototype.then = function(onFulfilled, onRejected) {
return new Promise(function(resolve,reject) { //此處的Promise標示為promise2
句柄({
onFulfilled: onFulfilled,
onRejected: onRejected,
解:解,
拒絕:拒絕
})
});
}
函數句柄(延遲){
varhandleFn;
if(state === '完成') {
handleFn = deferred.onFulfilled;
} else if(state === '拒絕') {
handleFn = deferred.onRejected;
}
var ret = handleFn(value);
deferred.resolve(ret); }
函數解析(val){
if(val && typeof val.then === '函數') {
val.then(解決); 地 返回;
}
if(callback) { 回呼(val);
}
}
b)實現了多種不同非同步程式庫之間的轉換。
1.4.3 commonJS Promise/A 規範
1.4.4 注意事項
});
p.then(函數(val) {
console.log('第二次回呼:' val.x)
})
// 第一個回呼:1
// 第二個回呼: 2
1.5 Generator
以上的所有方法都是基於回呼函數來完成非同步操作的,無非是對回呼函數進行封裝而已。 ES6裡面提出了Generator,增加了解決非同步操作的途徑,不再依據回呼函數來完成。
Generator最大的特色是可實現函數的暫停、重啟,而這個特性非常有利於解決非同步操作。將Generator的暫停與promise的異常處理結合起來,可以比較優雅地解決非同步程式設計問題。具體實現參考:Kyle Simpson
2. 非同步程式設計存在的問題
2.1 異常處理
a) 非同步事件包含兩個環節:發出非同步請求、結果處理,這兩個環節透過event loop連結。那麼try catch來進行異常捕捉的時候就需要分開來捕捉。
try {
asyncEvent(callback);
} catch(err) {
......
}
上述程式碼是無法捕捉callback裡面的異常,只能取得發出請求環節的異常。這樣就存在問題:假如請求的發出和請求的處理是兩個人完成的,那麼在異常處理的時候就存在問題?
b)promise實現異常的傳遞,這帶來一些好處,並在實際專案中確保程式碼不會被阻塞。但是如果非同步事件比較多的時候,不容易找出到底是那個非同步事件產生了異常。
// 場景描述: 在CRM裡面展示價格的警報訊息,其中包含競對的訊息。但是取得競對的資訊時間比較長,後端為了避免慢查詢,就把一筆記錄拆成兩塊分別獲取。
// 第一步:取得價格警報訊息,除了競對訊息
function getPriceAlarmData() {
return new Promise(function(resolve) {
Y.io(url, {
method: 'get',
data: params,
on: function() {
success: function(id, data) {
resolve(alarmData);
}
}
});
});
}
// 拿到警報訊息後,在去獲取競對訊息
getPriceAlarmData().then(function(data) {
// 資料渲染,除了競對資訊
render(data);
return new Promise(function(resolve) {
Y.io(url, {
method: 'get',
data: {alarmList: data},
on: function() {
success: function(id, compData) {
resolve(compData);
}
}
});
});
}) // 取得所有資料後進行競對資訊的渲染
.then(function(data) {
// 渲染競對資訊
render(data)
}, function(err) {
// 異常處理
console.log(err);
});
可以將上述程式碼轉換成如下:
try{
// 取得競對以外的警報資訊
var alarmData = alarmDataExceptCompare();
render(alarmData);
// 根據警報資訊查詢競對資訊
var compareData = getCompareInfo(alarmData);
render(compareData);
} catche(err) {
console.log(err.message);
}
在上述例子中把異常處理放到最後進行處理,這樣當其中存在某個環節出現異常,我們無法準確知道到底是哪個事件產生的。
2.2 jQuery.Deferred 的問題
jQuery中也實現了非同步操作,但是在實作上不符合promise/A 規範,主要表現在以下幾個方面:
a. 參數的個數:標準的Promise只能接受一個參數,而jQuery中則可以傳遞多個參數
function asyncInJQuery() {
var d = new $.Deferred();
setTimeout(function() {
d.resolve(1, 2);
}, 100);
return d.promise()
}
asyncInJQuery().then(function(val1, val2) {
console.log('output: ', val1, val2);
});
// output: 1 2
b. 結果處理中異常的處理
function asyncInPromise() {
return new Promise(function(resolve) {
setTimeout(function() {
var jsonStr = '{"name": "mt}';
resolve(jsonStr);
}, 100);
});
}
asyncInPromise().then(function(val) {
var d = JSON.parse(val);
console.log(d.name);
}).then(null, function(err) {
console.log('show error: ' err.message);
});
// show error: Unexpected end of input
function asyncInJQuery() {
var d = new $.Deferred();
setTimeout(function() {
var jsonStr = '{"name": "mt}';
d.resolve(jsonStr);
}, 100);
return d.promise()
}
asyncInJQuery().then(function(val) {
var d = JSON.parse(val);
console.log(d.name);
}).then(function(v) {
console.log('success: ', v.name);
}, function(err){
console.log('show error: ' err.message);
});
//Uncaught SyntaxError: Unexpected end of input
從中可以看出,Promise對回呼函數進行了結果處理,可以捕捉回呼函數執行過程中的異常,而jQuery.Deferred卻不可以。

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

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

Node.js 安裝目錄中有兩個與 npm 相關的文件:npm 和 npm.cmd,區別如下:擴展名不同:npm 是可執行文件,npm.cmd 是命令視窗快捷方式。 Windows 使用者:npm.cmd 可以在命令提示字元中使用,npm 只能從命令列執行。相容性:npm.cmd 特定於 Windows 系統,npm 跨平台可用。使用建議:Windows 使用者使用 npm.cmd,其他作業系統使用 npm。

要連接 MySQL 資料庫,需要遵循以下步驟:安裝 mysql2 驅動程式。使用 mysql2.createConnection() 建立連接對象,其中包含主機位址、連接埠、使用者名稱、密碼和資料庫名稱。使用 connection.query() 執行查詢。最後使用 connection.end() 結束連線。

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

Node.js 和 Java 的主要差異在於設計和特性:事件驅動與執行緒驅動:Node.js 基於事件驅動,Java 基於執行緒驅動。單執行緒與多執行緒:Node.js 使用單執行緒事件循環,Java 使用多執行緒架構。執行時間環境:Node.js 在 V8 JavaScript 引擎上運行,而 Java 在 JVM 上運行。語法:Node.js 使用 JavaScript 語法,而 Java 使用 Java 語法。用途:Node.js 適用於 I/O 密集型任務,而 Java 適用於大型企業應用程式。

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

Node.js 適用於以下專案類型:網頁和伺服器應用程式事件驅動應用程式即時應用程式資料密集型應用程式命令列工具和腳本輕量級微服務
