S200 S200 - 4 months ago 18
Javascript Question

Node.js: Promise resolved before resolve() is called?

I'm very new to the world of Node.js and its asynchronous behaviors.

I am trying to take a file, save a copy of it, append a line to the copied file and start using the copied file - in that sequence.

This is roughly what I have right now...

var first = new Promise(function(resolve, reject) {
console.log("1");
var readStream = fs.createReadStream("file.txt");
var writeStream = fs.createWriteStream("file-copy.txt");
readStream.on("end", function () {
writeStream.end();
});

var pipeAction = readStream.pipe(writeStream);
pipeAction.on("close", function() {
console.log("2");
resolve();
});
});

var second = new Promise(function(resolve, reject) {
console.log("3");
fs.appendFile("file-copy.txt", "\nA NEW LINE TO INSERT", function (err) {

});
console.log("4");
resolve();
});

var third = new Promise(function(resolve, reject) {
// do something with the modified, copied file
console.log("5");
});

first.then(second).then(third);


The output shows 1 3 4 5 2 instead of 1 2 3 4 5. Can anyone share some lights on why "first" was able to resolve before "2" was printed?

Thanks!

Answer

You're using promises incorrectly. In your example, you are immediately invoking your promises. Since their executor function (the callback with resolve/reject params) is called the moment a promise is instantiated, you're immediately logging 1,3,4, and 5. Since console.log('2') is wrapped in an async action's callback, it's being deferred and is ultimately being called last.

The correct way to implement what you're attempting is to set your variables to be functions themselves and return your promises instead. This way, when your first, second, and third functions are called, they immediately invoke their executors and start the control flow. It's imperative that you return the promises though, or you will break the promise chain.

function first() {
  return new Promise(function(resolve, reject) {
    console.log("1");
    var readStream = fs.createReadStream("file.txt");
    var writeStream = fs.createWriteStream("file-copy.txt");
    readStream.on("end", function() {
      writeStream.end();
    });

    var pipeAction = readStream.pipe(writeStream);
    pipeAction.on("close", function() {
      console.log("2");
      resolve();
    });
  });
}

function second() {
  return new Promise(function(resolve, reject) {
    console.log("3");
    fs.appendFile("file-copy.txt", "\nA NEW LINE TO INSERT", function(err) {

    });
    console.log("4");
    resolve();
  });
}

function third() {
  return new Promise(function(resolve, reject) {
    // do something with the modified, copied file
    console.log("5");
  });
}

first().then(second).then(third);

Yields:

1
2
3
4
5

Here's a working version to play around with:

function first() {
  return new Promise((resolve, reject) => {
    console.log(1);
    // async request
    setTimeout(() => {
      console.log(2);
      resolve();
    }, 1000);
  });
}

function second() {
  return new Promise((resolve, reject) => {
    console.log(3);
    // async request
    setTimeout(() => {
      console.log(4);
      resolve();
    }, 1000);
  });
}

function third() {
  return new Promise((resolve, reject) => {
    console.log(5);

    resolve();
  });
}

first()
  .then(second)
  .then(third);

Comments