使用events.EventEmitter 控制Node.js 程序執行流程html
標題寫的可能也不太對,你們領會精神;node
Node.js 是一個基於Chrome JavaScript 運行時創建的一個平臺。編程
Node.js是一個事件驅動I/O服務端JavaScript環境,基於Google的V8引擎,V8引擎執行Javascript的速度很是快,性能很是好。json
Node.js 異步編程的直接體現就是回調。設計模式
異步編程依託於回調來實現,但不能說使用了回調後程序就異步化了。數組
回調函數在完成任務後就會被調用,Node 使用了大量的回調函數,Node 全部 API 都支持回調函數。併發
例如,咱們能夠一邊讀取文件,一邊執行其餘命令,在文件讀取完成後,咱們將文件內容做爲回調函數的參數返回。異步
這樣在執行代碼時就沒有阻塞或等待文件 I/O 操做。這就大大提升了 Node.js 的性能,能夠處理大量的併發請求。異步編程
Node.js 是單進程單線程應用程序,可是由於 V8 引擎提供的異步執行回調接口,經過這些接口能夠處理大量的併發,因此性能很是高。函數
Node.js 幾乎每個 API 都是支持回調函數的。
Node.js 基本上全部的事件機制都是用設計模式中觀察者模式實現。
Node.js 單線程相似進入一個while(true)的事件循環,直到沒有事件觀察者退出,每一個異步事件都生成一個事件觀察者,若是有事件發生就調用該回調函數。
大意就是說:與大多數 代碼從上至下線性執行的 程序不一樣,Node.js 程序不會「等」,
當程序執行到須要I/O執行時間的回調函數時,會主動略過等待,繼續執行下面的代碼;
所以就會出現一種弊端:一個特定函數的輸入(參數),是數個回調函數的返回;
程序的執行機制會略過 對回調函數的等待,直接執行下面的特定函數;此時特定函數並無等到回調函數返回的輸入;
例子以下:
var fs = require("fs");
var array = [1, 2, 3, 4, 5]; var json = {};
for (let index = 0; index < array.length; index++) { const element = array[index]; deal(element, function (ret) { json[element] = ret; console.log(json); }); } console.log(json); function deal(num, callback) { fs.readFile(num + '.txt', function (err, data) { if (err) { console.error(err); callback(err); } else { callback(data.toString().trim()); } }); } Object {} Object {1: "11111"} Object {1: "11111", 5: "55555"} Object {1: "11111", 3: "33333", 5: "55555"} Object {1: "11111", 3: "33333", 4: "44444", 5: "55555"} Object {1: "11111", 2: "22222", 3: "33333", 4: "44444", 5: "55555"}
回調函數要以數組裏的內容做爲文件名,讀取文件中的數據做爲返回值;
但程序不會等待文件讀取完成,在沒有一個文件讀取完畢的狀況下就執行了打印操做,輸出了空對象;
若是想要按順序線性執行文件讀取,在讀取完畢後打印輸出,不只浪費時間,並且一層套一層代碼冗餘,
還沒法預估數組大小(回調函數個數)。
Node.js 全部的異步 I/O 操做在完成時都會發送一個事件到事件隊列。
解決的辦法是:Node.js 的事件驅動特性,讓打印做爲一個事件,在全部的回調函數執行完後,再觸發打印操做;
var fs = require("fs"); var events = require('events'); var emitter = new events.EventEmitter(); var array = [1, 2, 3, 4, 5]; var count = 0; var json = {}; for (let index = 0; index < array.length; index++) { const element = array[index]; deal(element, function (ret) { json[element] = ret; console.log(json); }); } emitter.on('dataBack', function() { count ++; if(count == array.length){ console.log(json); } }); function deal(num, callback) { fs.readFile(num + '.txt', function (err, data) { if (err) { console.error(err); callback(err); emitter.emit('dataBack'); } else { callback(data.toString().trim()); emitter.emit('dataBack'); } }); } Object {1: "11111"} Object {1: "11111", 4: "44444"} Object {1: "11111", 3: "33333", 4: "44444"} Object {1: "11111", 3: "33333", 4: "44444", 5: "55555"} Object {1: "11111", 2: "22222", 3: "33333", 4: "44444", 5: "55555"} Object {1: "11111", 2: "22222", 3: "33333", 4: "44444", 5: "55555"}
在程序中添加打印事件和事件觸發,所有數據都集齊後再打印,
雖然還有count的鎖不鎖的問題存在,但實際的問題已然解決;
嗯嗯。
引用和改寫的程序都來源於 Node.js 教程 | 菜鳥教程
不不不,一點也不推薦你們這麼用。。。
太low了 用Promise吧
function run_a(num) { return new Promise(function (resolve, reject) { if (!isNaN(num)) { resolve({ "number": num, "10*num": 10 * num }) } else { reject({ "error": num + " is not a number!" }) } }); } var array = [1, 2, 3, 4, 5, 6, 7]; var function_array = []; array.forEach(element => { function_array.push(run_a(element)); }); console.log(function_array); // Array(7) [Promise, Promise, Promise, Promise, Promise, Promise, Promise] Promise.all(function_array).then(function (data) { console.log(JSON.stringify(data)); // [{"number":1,"10*num":10},{"number":2,"10*num":20},{"number":3,"10*num":30},{"number":4,"10*num":40}, // {"number":5,"10*num":50},{"number":6,"10*num":60},{"number":7,"10*num":70}] }, function (data) { console.log(data); });
var array = [1, 2, 3, 4, 5, 'a', 7]; var function_array = []; array.forEach(element => { function_array.push(run_a(element)); }); Promise.all(function_array).then(function (data) { console.log(JSON.stringify(data)); }, function (data) { console.log(data); // {error: "a is not a number!"} });
用Promise應對這種未知次數的併發等待問題能夠說是很合適的了。
J.X.Duasonir