简体   繁体   English

JavaScript为什么有些代码在其余部分之前执行?

[英]JavaScript Why is some code getting executed before the rest?

I've mostly learned coding with OOPs like Java. 我主要学习使用像Java这样的OOP进行编码。

I have a personal project where I want to import a bunch of plaintext into a mongodb. 我有一个个人项目,我想将一堆明文导入mongodb。 I thought I'd try to expand my horizons and do this with using node.js powered JavaScript. 我以为我会尝试扩展我的视野,并使用node.js支持的JavaScript来做到这一点。

I got the code working fine but I'm trying to figure out why it is executing the way it is. 我得到的代码工作正常,但我想弄清楚它为什么执行它的方式。

The output from the console is: 1. done reading file 2. closing db 3. record inserted (n times) 控制台的输出是: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();
});

(full code is here: https://github.com/HansHovanitz/Import-Stuff/blob/master/importStuff.js ) (完整代码在这里: https//github.com/HansHovanitz/Import-Stuff/blob/master/importStuff.js

When I run it I get the message 'done reading file' and 'closing db' and then all of the 'record inserted' messages. 当我运行它时,我得到消息'完成读取文件'和'关闭数据库',然后是所有'记录插入'消息。 Why is that happening? 为什么会这样? Is it because of the delay in inserting a record in the db? 是因为在数据库中插入记录的延迟? The fact that I see 'closing db' first makes me think that the db would be getting closed and then how are the records being inserted still? 我首先看到'关闭数据库'的事实让我认为数据库将被关闭,然后如何插入记录?

Just curious to know why the program is executing in this order for my own peace of mind. 只是好奇地知道为什么程序按此顺序执行以便我自己安心。 Thanks for any insight! 感谢您的任何见解!

In short, it's because of asynchronous nature of I/O operations in the used functions - which is quite common for Node.js. 简而言之,这是因为在使用的函数中I / O操作的异步性 - 这对Node.js来说很常见。

Here's what happens. 这是发生了什么。 First, the script reads all the lines of the file, and for each line initiates db.insertOne() operation, supplying a callback for each of them. 首先,脚本读取文件的所有行,并为每行启动db.insertOne()操作,为每个行提供回调 Note that the callback will be called when the corresponding operation is finished , not in the middle of this process. 请注意,当相应的操作完成时,将调用回调,而不是在此过程的中间。

Eventually the script reaches the end of the input file, logs two messages, then invokes db.close() line. 最终脚本到达输入文件的末尾,记录两条消息,然后调用db.close()行。 Note that even though 'insert' callbacks (that log 'inserted' message) are not called yet, the database interface has already received all the 'insert' commands . 请注意,即使尚未调用 “插入” 回调 (该日志“插入”消息),数据库接口也已收到所有“插入” 命令

Now the tricky part: whether or not DB interface succeeds to store all the DB records (in other words, whether or not it'll wait until all the insert operations are completed before closing the connection) is up both to DB interface and its speed. 现在棘手的部分:数据库接口是否成功存储所有数据库记录(换句话说,它是否会等到关闭连接之前所有插入操作都完成)都是数据库接口及其速度。 If write op is fast enough (faster than reading the file line), you'll probably end up with all the records been inserted; 如果写操作足够快(比读取文件行更快),你可能最终会插入所有记录; if not, you can miss some of them. 如果没有,你可以错过其中一些。 That's why it's a safest bet to close the connection to database not in the file close (when the reading is complete), but in insert callbacks (when the writing is complete): 这就是为什么关闭与数据库的连接而不是文件关闭(当读取完成时),但在插入回调 (写入完成时)时最安全的选择:

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;
});

This question describes the similar problem - and several different approaches to solve it. 这个问题描述了类似的问题 - 以及解决它的几种不同方法。

Welcome to the world of asynchronicity. 欢迎来到异步的世界。 Inserting into the DB happens asynchronously. 插入数据库是异步发生的。 This means that the rest of your (synchronous) code will execute completely before this task is complete. 这意味着您的(同步)代码的其余部分将在此任务完成之前完全执行。 Consider the simplest asynchronous JS function setTimeout . 考虑最简单的异步JS函数setTimeout It takes two arguments, a function and a time (in ms) after which to execute the function. 它需要两个参数,一个函数和一个时间(以ms为单位),然后执行该函数。 In the example below "hello!" 在下面的例子中,“你好!” will log before "set timeout executed" is logged, even though the time is set to 0. Crazy right? 将记录“设置超时执行”之前记录,即使时间设置为0.疯狂吧? That's because setTimeout is asynchronous. 那是因为setTimeout是异步的。

This is one of the fundamental concepts of JS and it's going to come up all the time, so watch out! 这是JS的基本概念之一,它会一直出现,所以要小心!

setTimeout(() => {
  console.log("set timeout executed") 
}, 0) 

console.log("hello!")

When you call db.collection("quotes").insertOne you're actually creating an asynchronous request to the database, a good way to determine if a code will be asynchronous or not is if one (or more) of its parameters is a callback. 当你调用db.collection("quotes").insertOne你实际上是在创建一个对数据库的异步请求,一个确定代码是否异步的好方法是它的一个(或多个)参数是否是打回来。

So the order you're running it is actually expected: 所以你运行它的顺序实际上是预期的:

  1. You instantiate rl 你实例化rl
  2. You bind your event handlers to rl 您将事件处理程序绑定到rl
  3. Your stream starts processing & calling your 'line' handler 您的流开始处理并调用您的'line'处理程序
  4. Your 'line' handler opens asynchronous requests 你的'line'处理程序打开异步请求
  5. Your stream ends and rl closes 您的流结束并且rl关闭

    ... ...

4.5. 4.5。 Your asynchronous requests return and execute their callbacks 您的异步请求返回并执行其回调

I labelled the callback execution as 4.5 because technically your requests can return at anytime after step 4. 我将回调执行标记为4.5,因为从技术上讲,您的请求可以在步骤4之后的任何时间返回。

I hope this is a useful explanation, most modern javascript relies heavily on asynchronous events and it can be a little tricky to figure out how to work with them! 我希望这是一个有用的解释,大多数现代javascript在很大程度上依赖于异步事件,并且弄清楚如何使用它们可能有点棘手!

You're on the right track. 你走在正确的轨道上。 The key is that the database calls are asychronous. 关键是数据库调用是异步的。 As the file is being read, it starts a bunch of async calls to the database. 在读取文件时,它会启动一堆对数据库的异步调用。 Since they are asynchronous, the program doesn't wait for them to complete at the time they are called. 由于它们是异步的,因此程序不会在它们被调用时等待它们完成。 The file then closes. 然后文件关闭。 As the async calls complete, your callbacks runs and the console.logs execute. 当异步调用完成时,运行回调并执行console.logs。

Your code reads lines and immediately after that makes a call to the db - both asynchronous processes. 您的代码读取行,然后立即调用db - 两个异步进程。 When the last line is read the last request to the db is made and it takes some time for this request to be processed and the callback of the insertOne to be executed. 读取最后一行时,将对db进行最后一次请求,并且需要一些时间来处理此请求并执行insertOne的回调。 Meanwhile the r1 has done it's job and triggers the close event. 与此同时, r1完成了它的工作并触发了close事件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM