這裡說併發異步,並不準確,應該說連續異步。 NodeJs單線程異步的特性,直接導致多個非同步同時進行時,無法確定最後的執行結果來回調。舉個簡單的例子:
for(var i = 0; i < 5; i++) { fs.readFile('file', 'utf-8', function(error, data){}); }
連續發起了5次讀取檔案的非同步操作,很簡單,那麼問題來了,我怎麼確定所有非同步都執行完了呢?因為要在它們都執行完後,才能進行之後的操作。相信有點經驗的同學都會想到用記數的方式來進行,但如何保證記數正確又是一個問題。仔細想想:
回呼是一個函數,每個非同步操作時將計數器+1,當每個非同步結束時將計數器-1,透過判斷計數器是否為0來決定是否執行回呼。這個邏輯很簡單,需要一個相對於執行時和回調時的全域變數作為計數器,而且要在傳給非同步方法是執行+1的操作,而且之後將返回一個用來回調的函數,有點繞,不過看看Js函數的高階用法:
var pending = (function() { var count = 0; return function() { count++; return function() { count--; if (count === 0) { // 全部执行完毕 } } } });
當pending呼叫時,即pending(),例如:
var done = pending();
這時計數變數count即被初始化為0,則傳回的函數附給了done,這時如果執行done(),會是什麼?是不是直接執行pending回傳的第一個函數,即:pending()(),這個執行又是什麼,首先將計數變數count+1,又傳回了一個函數,這個函數直接當做callback傳給非同步的方法,執行這個callback的時候,首先是將計數變數count-1,再判斷count是否為0,如果為0即表示所有的非同步執行完成了,從而達到連續的非同步,同一回呼的操作。
關鍵就在兩個return上,簡單的說:
第一個return的函數是將count+1,接著傳回需要回呼的函數
第二個return的函數就是需要回呼的函數,如果它執行,就是將count-1,然後判斷非同步是否全部執行完成,完成了,就回呼
看個實際點的例子,讀取多個檔案的非同步回呼:
var fileName = ['1.html', '2.html', '3.html']; var done = pending(function(fileData) { console.log('done'); console.log(fielData); }); for(var i = 0; i < fileName.lenght; i++) { fs.readFile(fileName[i], 'utf-8', done(fileName[i])); }
其中的done,即用pending方法包起了我們想回調執行的方法,當計數器為0時,就會執行它,那我們得改進一下pending方法:
var pending = (function(callback) { var count = 0; var returns = {}; console.log(count); return function(key) { count++; console.log(count); return function(error, data) { count--; console.log(count); returns[key] = data; if (count === 0) { callback(returns); } } } });
callback即為我們的回呼函數,當var done = pending(callback)時,done其實已為第一個return的函數,它有一個參數,可以當做返回的值的下標,所以在循環體中done(fileName[i]),把檔名傳了進去。這個done()是直接執行的,它將count+1後,回傳了要傳給非同步方法的回呼函數,如前面所說,這個回呼函數裡會根據計數變數來判斷是否執行我們希望執行的回呼函數,而且把文件的內容傳給了它,即returns。好了,運行一下,相信能夠準確的看到運行結果。
0
1
2
3
2
1
0
done
{"1.html": "xxx", "2.html": "xxx", "3.html": "xxx"}
從計數上明顯能看出,從0-3再到0,之後就是我們的回呼函數輸出了done和檔案的內容。
這個問題解決了,我們要思考一下,如何讓這樣的方法封裝重用,不然,每次都寫pending不是很不科學嗎?
下面看看UnJs(我的一個基於NodeJs的Web開發框架)的處理方式,應用於模板解析中的子模板操作:
unjs.asyncSeries = function(task, func, callback) { var taskLen = task.length; if (taskLen <= 0) { return; } var done = unjs.pending(callback); for(var i = 0; i < taskLen; i++) { func(task[i], done); } }
asyncSeries有三個參數,意思是:
task: 需要處理的對象,例如需要讀取的文件,它是一個列表,如果不是列表,或列表長度為0,它將不會執行
func: 非同步方法,如fs.readFile,就是經由它傳進去的
callback: 我們希望回呼的方法
done和前面同理,它傳給了func,但並沒有執行,因為希望應用端能控制參數,所以讓應用端去執行。
再看看處理子範本時的操作:
var subTemplate = []; var patt = /\{\% include \'(.+)\' \%\}/ig; while(sub = patt.exec(data)) { var subs = sub; subTemplate.push([subs[0], subs[1]]); } unjs.asyncSeries(subTemplate, function(item, callback) { fs.readFile('./template/' + item[1], 'utf-8', callback(item[0])); }, function(data) { for(var key in data) { html = html.replace(key, data[key]); } });
subTemplate這個列表,是根據對子模板的解析生成的數據,它是一個二維的數組,每個子項的第一個值為子模板的調用文本,即:{% include 'header.html ' %}這樣的字串,第二個參數為子模板檔名,即:header.html
asyncSeries的第二個參數是的callback,實際上是第三個參數,也就是我們希望執行的回呼函數經過pending處理的回調方法,如前面所說,在asyncSeries內部,它並沒有運行,而是到這裡運行的,即:callback(item[0]),帶上了參數,因為後面還要根據這個參數將父模板中調用子模板的字串替換為對應子模板的內容。
這樣子,只要需要連續非同步時,就可以使用asyncSeries方法來處理了。因為非同步的關係,程式的流程有點繞,可能開始不太好理解,即使熟悉了,也有可能突然想不明白,沒關係,比如,第二個參數中的callback實際上是第三個參數生成的,開始可能你就會想,這個callback倒底是啥。還有就是pending的兩個return,也是不太好理解的,需要多想想。
好了,連續非同步的回呼使用Js函數的高階特性完成了。但NodeJs的非同步性著實讓程式的控制很成問題,諸如還有連續異步,但要傳值的操作等,這些都是可以透過這樣的思路,變化一下即可實現的。
以上內容是小編給大家分享的NodeJs並發異步的回調處理的相關知識,希望大家喜歡。

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

SublimeText3 Linux新版
SublimeText3 Linux最新版