Node.js 非同步程式設計之 Callback介紹(一)_node.js
Node.js 基於 JavaScript 引擎 v8,是單執行緒的。 Node.js 採用了與通常 Web 上的 JavaScript 非同步程式設計的方式來處理會造成阻塞的I/O操作。在 Node.js 中讀取檔案、存取資料庫、網路請求等等都有可能是異步的。對於 Node.js 新人或從其他語言背景遷移到 Node.js 上的開發者來說,非同步程式設計是比較痛苦的一部分。本章將由淺入深為大家講解 Node.js 非同步程式設計的方方面面。從最基礎的 callback 到 thunk、Promise、co 直到 ES7 計劃的 async/await。
首先我們先從一個具體的非同步程式設計的例子說起。
取得多個 ip 所在地的天氣資訊
在 ip.json 這個檔案中,有一個陣列我們存放了若干個 ip 位址,分別來自不同的地方的不同訪客,內容如下:
// ip.json
["115.29.230.208", "180.153.132.38", "74.125.235.224", "91.239.201.98", "60.28.215.115"]
希望可以每一個 ip 所在地當前的天氣。將結果輸出到 weather.json 這個檔案中各式如下:
// weather.json
[
{ "ip": "115.29.230.208", "weather": "Clouds", "region": "Zhejiang" },
{ "ip": "180.153.132.38", "weather": "Clear", "region": "Shanghai" },
{ "ip": "74.125.235.224", "weather": "Rain", "region": "California" },
{ "ip": "60.28.215.115", "weather": "Clear", "region": "Tianjin" }
]
整理思路,我們分成以下幾步來完成:
1.讀取 ip 位址;
2.依據 ip 位址取得 ip 所在地的地理位置;
3.依地理位置查詢當地的天氣;
4.將結果寫入 weather.json 檔案中。
這些步驟都是非同步的(讀寫檔案可以同步,但作為範例,都用非同步)。
callback
首先我們嘗試不借助任何函式庫,試著以 Node.js API 通常提供的方式——專遞一個 callback 作為非同步回呼——來實現。我們將藉助三個基礎模組:
1.fs:從檔案 ip.json 讀取 IP 清單;把結果寫入檔案;
2.request:用來發送 HTTP 請求,根據 IP 位址取得 geo 數據,再透過 geo 數據取得天氣數據;
3.querystring:用來組裝發送請求的 url 參數。
新建一個 callback.js 文件,引入這幾個模組:
// callback.js
var fs = require('fs')
var request = require('request')
var qs = require('querystring')
讀取檔案中的 IP 列表,呼叫 fs.readFile 讀取檔案內容,再透過 JSON.parse 解析 JSON 資料:
...
function readIP(path, callback) {
fs.readFile(path, function(err, data) {
if (err) {
callback(err)
} else {
try {
data = JSON.parse(data)
callback(null, data)
} catch (error) {
callback(error)
}
}
})
}
...
接著就是使用 IP 來取得geo,我們使用 request 來請求一個開放的 geo 服務:
...
function ip2geo(ip, callback) {
var url = 'http://www.telize.com/geoip/' ip
request({
url: url,
json: true
}, function(err, resp, body) {
callback(err, body)
})
}
...
使用 geo 資料來取得 weather:
...
function geo2weather(lat, lon, callback) {
var params = {
lat: lat,
lon: lon,
APPID: '9bf4d2b07c7ddeb780c5b32e636c679d'
}
var url = 'http://api.openweathermap.org/data/2.5/weather?' qs.stringify(params)
request({
url: url,
json: true,
}, function(err, resp, body) {
callback(err, body)
})
}
...
現在我們已經取得 geo、取得 weather 的接口,接下來我們還有稍微複雜的問題要處理,因為 ip 有多個,所以我們需要並行地去讀取 geo 已經並行地讀取 weather 資料:
...
function ips2geos(ips, callback) {
var geos = []
var ip
var remain = ips.length
for (var i = 0; i ip = ips[i];
(function(ip) {
ip2geo(ip, function(err, geo) {
if (err) {
callback(err)
} else {
geo.ip = ip
geos.push(geo)
remain--
}
if (remain == 0) {
callback(null, geos)
}
})
})(ip)
}
}
function geos2weathers(geos, callback) {
var weathers = []
var geo
var remain = geos.length
for (var i = 0; i
geo = geos[i];
(function(geo) {
geo2weather(geo.latitude, geo.longitude, function(err, weather) {
if (err) {
callback(err)
} else {
weather.geo = geo
weathers.push(weather)
remain--
}
if (remain == 0) {
callback(null, weathers)
}
})
})(geo)
}
}
...
ips2geos 和 geos2weathers 都使用了比較原始的方法,remain 來計算等待傳回的個數,remain 為 0 表示並行請求結束,將處理結果裝進一個陣列傳回。
最後就是將結果寫入 weather.json 檔案:
...
function writeWeather(weathers, callback) {
var output = []
var weather
for (var i = 0; i weather = weathers[i]
output.push({
ip: weather.geo.ip,
weather: weather.weather[0].main,
region: weather.geo.region
})
}
fs.writeFile('./weather.json', JSON.stringify(output, null, ' '), callback)
}
...
組合上面這些函數,我們就可以實現我們的目標:
...
函數處理程序錯誤(錯誤){
console.log('錯誤:' err)
}
readIP('./ip.json', function(err, ips) {
如果(錯誤){
處理程序錯誤(錯誤)
} 其他 {
ips2geos(ips, 函數(錯誤, geos) {
如果(錯誤){
處理程序錯誤(錯誤)
} 其他 {
geos2weathers(geos, 函數(錯誤, 天氣) {
若(錯誤){
處理程序錯誤(錯誤)
} 其他 {
writeWeather(天氣, 函數(錯誤) {
若(錯誤){
處理程序錯誤(錯誤)
} 其他 {
console.log('成功!')
}
})
}
})
}
})
}
})
哈哈,你媽這句話,你可能覺得這就是 JavaScript 異步的問題,說真的,不是 JavaScript 異步的真正下面問題所在。上面程式碼我們可以這樣寫:
...
函數 ReadIPCallback(err, ips) {
如果(錯誤){
處理程序錯誤(錯誤)
} 其他 {
ips2geos(ips, ips2geosCallback)
}
}
函數 ips2geosCallback(err, geos) {
如果(錯誤){
處理程序錯誤(錯誤)
} 其他 {
geos2weathers(geos, geos2weathersCallback)
}
}
函數 geos2weathersCallback(err, 天氣) {
如果(錯誤){
處理程序錯誤(錯誤)
} 其他 {
writeWeather(天氣, writeWeatherCallback)
}
}
函數 writeWeatherCallback(err) {
如果(錯誤){
處理程序錯誤(錯誤)
} 其他 {
console.log('成功!')
}
}
readIP('./ip.json', ReadIPCallback)
好了,這就是我們callback.js的全部內容。運行:
節點回調.js
將會產生weater.json檔:
[
{
"ip": "180.153.132.38",
"天氣": "晴",
"地區": "上海"
},
{
"ip": "91.239.201.98",
「天氣」:「雲」
},
{
"ip": "60.28.215.115",
"天氣": "晴",
"地區": "天津"
},
{
"ip": "74.125.235.224",
“天氣”:“雲”,
「地區」:「加州」
},
{
"ip": "115.29.230.208",
"天氣": "晴",
"地區": "浙江"
}
]
那正真的問題是什麼?
當然是異步的問題啦,非同步本質上要處理三個事情:
1.非同步操作何時結束,需要通知回來,Callback 是方案;
2.非同步產生的結果需要傳遞回來,Callback 接受一個 data 參數,把資料傳回來;
3.異步如果出錯了怎麼辦? Callback 接受 一個 err 參數,把錯誤傳回來。
但有沒有發現好多重複的工作(各種 callback)?上面的這些程式碼有什麼問題麼?請大家期待本文的續篇。

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

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

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

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

Dreamweaver CS6
視覺化網頁開發工具

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

熱門話題

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

隨著Web應用程式變得越來越複雜,程式設計師不得不採用非同步程式來處理大量請求和I/O操作。 PHP:HypertextPreprocessor也不例外。為了滿足這項需求,ReactPHP已成為目前最受歡迎的PHP非同步程式設計框架之一。在本文中,將討論如何在PHP中使用ReactPHP進行非同步程式設計。 1.ReactPHP簡介ReactPHP是基於事件驅動編程

JavaScript函數非同步程式設計:處理複雜任務的必備技巧引言:在現代前端開發中,處理複雜任務已經成為了必不可少的一部分。而JavaScript函數非同步程式設計技巧則是解決這些複雜任務的關鍵。本文將介紹JavaScript函數非同步程式設計的基本概念和常用的實作方法,並提供具體的程式碼範例,幫助讀者更好地理解和使用這些技巧。一、非同步程式設計的基本概念在傳統的同步程式設計中,程式碼按

如何在PHP中實現非同步訊息處理引言:在現代的Web應用程式中,非同步訊息處理變得越來越重要。非同步訊息處理可以提高系統的效能和可擴展性,並改善使用者體驗。 PHP作為一種常用的伺服器端程式語言,也可以透過一些技術來實現非同步訊息處理。在本文中,我們將介紹一些PHP中實作非同步訊息處理的方法,並提供程式碼範例。使用訊息佇列訊息佇列是一種解耦系統元件的方式,它允許不同的元件在

深入理解PHP8的新特性:如何有效地使用非同步程式設計和程式碼? PHP8是PHP程式語言的最新主要版本,帶來了許多令人興奮的新功能和改進。其中最突出的特性之一是對非同步程式設計的支援。非同步程式設計可讓我們在處理並發任務時提高效能和回應能力。本文將深入探討PHP8的非同步程式設計特性,並介紹如何有效率地使用它們。首先,讓我們來了解一下什麼是非同步程式設計。在傳統的同步程式設計模型中,程式碼依照線性的順

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

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

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