簡體   English   中英

JavaScript為什么有些代碼在其余部分之前執行?

[英]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你實際上是在創建一個對數據庫的異步請求,一個確定代碼是否異步的好方法是它的一個(或多個)參數是否是打回來。

所以你運行它的順序實際上是預期的:

  1. 你實例化rl
  2. 您將事件處理程序綁定到rl
  3. 您的流開始處理並調用您的'line'處理程序
  4. 你的'line'處理程序打開異步請求
  5. 您的流結束並且rl關閉

    ...

4.5。 您的異步請求返回並執行其回調

我將回調執行標記為4.5,因為從技術上講,您的請求可以在步驟4之后的任何時間返回。

我希望這是一個有用的解釋,大多數現代javascript在很大程度上依賴於異步事件,並且弄清楚如何使用它們可能有點棘手!

你走在正確的軌道上。 關鍵是數據庫調用是異步的。 在讀取文件時,它會啟動一堆對數據庫的異步調用。 由於它們是異步的,因此程序不會在它們被調用時等待它們完成。 然后文件關閉。 當異步調用完成時,運行回調並執行console.logs。

您的代碼讀取行,然后立即調用db - 兩個異步進程。 讀取最后一行時,將對db進行最后一次請求,並且需要一些時間來處理此請求並執行insertOne的回調。 與此同時, r1完成了它的工作並觸發了close事件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM