[英]JavaScript Why is some code getting executed before the rest?
我主要學習使用像Java這樣的OOP進行編碼。
我有一個個人項目,我想將一堆明文導入mongodb。 我以為我會嘗試擴展我的視野,並使用node.js支持的JavaScript來做到這一點。
我得到的代碼工作正常,但我想弄清楚它為什么執行它的方式。
控制台的輸出是:1。完成讀取文件2.關閉db 3.記錄插入(n次)
var fs = require('fs'),
readline = require('readline'),
instream = fs.createReadStream(config.file),
outstream = new (require('stream'))(),
rl = readline.createInterface(instream, outstream);
rl.on('line', function (line) {
var split = line.split(" ");
_user = "@" + split[0];
_text = "'" + split[1] + "'";
_addedBy = config._addedBy;
_dateAdded = new Date().toISOString();
quoteObj = { user : _user , text : _text , addedby : _addedBy, dateadded : _dateAdded};
db.collection("quotes").insertOne(quoteObj, function(err, res) {
if (err) throw err;
console.log("record inserted.");
});
});
rl.on('close', function (line) {
console.log('done reading file.');
console.log('closing db.')
db.close();
});
(完整代碼在這里: https : //github.com/HansHovanitz/Import-Stuff/blob/master/importStuff.js )
當我運行它時,我得到消息'完成讀取文件'和'關閉數據庫',然后是所有'記錄插入'消息。 為什么會這樣? 是因為在數據庫中插入記錄的延遲? 我首先看到'關閉數據庫'的事實讓我認為數據庫將被關閉,然后如何插入記錄?
只是好奇地知道為什么程序按此順序執行以便我自己安心。 感謝您的任何見解!
簡而言之,這是因為在使用的函數中I / O操作的異步性 - 這對Node.js來說很常見。
這是發生了什么。 首先,腳本讀取文件的所有行,並為每行啟動db.insertOne()
操作,為每個行提供回調 。 請注意,當相應的操作完成時,將調用回調,而不是在此過程的中間。
最終腳本到達輸入文件的末尾,記錄兩條消息,然后調用db.close()
行。 請注意,即使尚未調用 “插入” 回調 (該日志“插入”消息),數據庫接口也已收到所有“插入” 命令 。
現在棘手的部分:數據庫接口是否成功存儲所有數據庫記錄(換句話說,它是否會等到關閉連接之前所有插入操作都完成)都是數據庫接口及其速度。 如果寫操作足夠快(比讀取文件行更快),你可能最終會插入所有記錄; 如果沒有,你可以錯過其中一些。 這就是為什么關閉與數據庫的連接而不是文件關閉(當讀取完成時),但在插入回調 (寫入完成時)時最安全的選擇:
let linesCount = 0;
let eofReached = false;
rl.on('line', function (line) {
++linesCount;
// parsing skipped for brevity
db.collection("quotes").insertOne(quoteObj, function(err, res) {
--linesCount;
if (linesCount === 0 && eofReached) {
db.close();
console.log('database close');
}
// the rest skipped
});
});
rl.on('close', function() {
console.log('reading complete');
eofReached = true;
});
這個問題描述了類似的問題 - 以及解決它的幾種不同方法。
歡迎來到異步的世界。 插入數據庫是異步發生的。 這意味着您的(同步)代碼的其余部分將在此任務完成之前完全執行。 考慮最簡單的異步JS函數setTimeout
。 它需要兩個參數,一個函數和一個時間(以ms為單位),然后執行該函數。 在下面的例子中,“你好!” 將記錄“設置超時執行”之前記錄,即使時間設置為0.瘋狂吧? 那是因為setTimeout是異步的。
這是JS的基本概念之一,它會一直出現,所以要小心!
setTimeout(() => {
console.log("set timeout executed")
}, 0)
console.log("hello!")
當你調用db.collection("quotes").insertOne
你實際上是在創建一個對數據庫的異步請求,一個確定代碼是否異步的好方法是它的一個(或多個)參數是否是打回來。
所以你運行它的順序實際上是預期的:
rl
rl
'line'
處理程序 'line'
處理程序打開異步請求 您的流結束並且rl
關閉
...
4.5。 您的異步請求返回並執行其回調
我將回調執行標記為4.5,因為從技術上講,您的請求可以在步驟4之后的任何時間返回。
我希望這是一個有用的解釋,大多數現代javascript在很大程度上依賴於異步事件,並且弄清楚如何使用它們可能有點棘手!
你走在正確的軌道上。 關鍵是數據庫調用是異步的。 在讀取文件時,它會啟動一堆對數據庫的異步調用。 由於它們是異步的,因此程序不會在它們被調用時等待它們完成。 然后文件關閉。 當異步調用完成時,運行回調並執行console.logs。
您的代碼讀取行,然后立即調用db - 兩個異步進程。 讀取最后一行時,將對db進行最后一次請求,並且需要一些時間來處理此請求並執行insertOne
的回調。 與此同時, r1
完成了它的工作並觸發了close
事件。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.