简体   繁体   中英

Understanding asynchronous functions in node.js

I am learning nodejs. I am having some hard time understanding how asynchronous functions works. My question is related to the code below. I am trying to do the following things in the following exact same order:

  1. Open the file a.txt.
  2. Read it.
  3. Print its content.
  4. Close it and log that the file has been closed.
  5. Open it again.
  6. Overwrite it with new content.

The problem is that as per the output I am getting, it seems that I am not controlling the sequence of these event. This is the output I am getting in the console:

just read 21 bytes / this is my test files / just wrote 30 bytes /file close and ready for write

So, as you can see, for some reason the program is writing in the file before it log that the file was closed. I was trying to close it, log that it was closed and then to write in the file.

So I think I have a problem controlling the flow of events. Can you point out what am I doing wrong?

This the the code:

var fs = require('fs');

//What I am trying to do here is: open a file a.txt, read it, print its content and then //close the file and log that it has been closed.
//Then, open it again and overwrite it.

fs.open('a.txt', 'r', function(err, fd){
    if(err){throw err;}
    var readBuffer = new Buffer(1024);
    var bufferOffset = 0;
    var filePosition = 0;
    var readBufferLength = readBuffer.length;

    fs.read(fd, readBuffer, bufferOffset, readBufferLength, filePosition, function(err, readBytes){
    if(err){throw err;}
    console.log('just read ' + readBytes + ' bytes');
    console.log(readBuffer.slice(0,readBytes).toString());
    fs.close(fd,function(){
        console.log('file close and ready for write');
    });
    });




});


fs.open('a.txt', 'r+', function(err,fd){
    if(err){throw err;}
    var writeBuffer = new Buffer('saul lugo overwrote this file!');
    var bufferOffset = 0;
    var writeBufferLength = writeBuffer.length;
    var filePosition = null;

    fs.write(fd, writeBuffer, bufferOffset, writeBufferLength, filePosition, function(err, writeBytes){
    if(err){throw err;}
    if(writeBytes>0){
        console.log('just wrote ' + writeBytes + ' bytes.');
    }
    });
});

All of those operations are asynchronous, so you cannot invoke fs.open('a.txt', 'r+') at the top-level of your code - it will be invoked immediately after fs.open('a.txt', 'r') which leads to the unexpected results you're getting.

Take a look at writeToAFile() which is getting invoked in a callback for first fs.close() . This is the key to making sure file is first being read, closed and then wrote to and closed.

Here is a fix:

var fs = require('fs');

fs.open('a.txt', 'r', function(err, fd){
    // do stuff ...

    fs.read(/* params */, function(err, readBytes){
      // do stuff ...

      fs.close(fd,function(){
        // now open file again for writing
        writeToAFile();
      });
    });
});

// This will be called inside the callback handler for closing the file.
// This way file will be opened for reading, read, close and THEN opened for writing.
function writeToAFile () {
  fs.open('a.txt', 'r+', function(err,fd){
    // do stuff ...

    fs.write(/* params */, function(err, writeBytes){
      // do stuff ...

      // close file
    });
  });
}

You need to wait until step 4 is done before calling fs.open again.

Right now your code kind of looks like

fs.open("a.txt", function(){
    foo(function(){
       console.log("done with first file")
    })
});

fs.open("a.txt", function(){
    foo(function(){
      console.log("done with second file")
    })
});

To preserve the order you need to nest the functions:

fs.open("a.txt", function(){
    foo(function(){
       console.log("done with first file")

       fs.open("a.txt", function(){
           foo(function(){
               console.log("done with second file")
           })
        });
    })
});

Of course, this now looks very ugly and neting 4+ levels deeps is hard to read. You can make it look a little better by creating extra named functions

  console.log("done with first file");
  doThingsWithSecondFile();

Or you could look into libraries like async.js or promises. (These libraries are specially useful if you want better error handling by default)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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