Matt
Matt

Reputation: 1757

node.js promises not forcing order execution of functions

I have three functions that I want to use promises to force them to execute in order.

function 1 sends a http request, fetches JSON data and saved it to a file function 2 loops through that file and updates the database according the difference values/values missing function 3 will loop through the newly updated database and create a 2nd json file.

Currently function 1 works perfectly on its own with a setInterval of 30 minutes.

I want to start function 2 when function 1 has finished. then function 3 after function 2 has finished.

Using promises I am trying to attach function 2 to a simple finished log to understand how to use promises but not getting much success. The items from the for loop log but my Finished/err log before my for loop which shouldn't be happening. Any suggestions?

function readJson() {
    return new Promise(function() {
        fs.readFile(__dirname + "/" + "bitSkin.json", 'utf8', function read(err, data) {
            if (err) { throw err; }
            var bitCon = JSON.parse(data);  

            for(var i=0; i<7; i++) { //bitCon.prices.length; i++) {
                var price = bitCon.prices[i].price
                var itemName = bitCon.prices[i].market_hash_name;
                (function() {
                    var iNameCopy = itemName;
                    var priceCopy = price;
                    logger.info(iNameCopy);
                }());
            }
        });
    });
};

function fin() {
    logger.info("Finished");
}

readJson().then(fin(), console.log("err"));

Upvotes: 2

Views: 172

Answers (1)

jfriend00
jfriend00

Reputation: 707376

Promises have no magical powers. They don't magically know when async code inside them is done. If you create a promise, you yourself have to resolve() or reject() it when the async code has an error or completes.

Then, in addition, you have to pass a function reference to a .then() handler, not the result of executing a function. .then(fin()) will call fin() immediately and pass it's return value to .then() which is not what you want. You want something like .then(fin).

Here's how you can resolve and reject the promise you created:

function readJson() {
    return new Promise(function(resolve, reject) {
        fs.readFile(__dirname + "/" + "bitSkin.json", 'utf8', function read(err, data) {
            if (err) { return reject(err); }
            var bitCon = JSON.parse(data);  

            for(var i=0; i<7; i++) { //bitCon.prices.length; i++) {
                var price = bitCon.prices[i].price
                var itemName = bitCon.prices[i].market_hash_name;
                (function() {
                    var iNameCopy = itemName;
                    var priceCopy = price;
                    logger.info(iNameCopy);
                }());
            }
            resolve(bitCon);
        });
    });
};

And, you could use that like this:

function fin() {
    logger.info("Finished");
}
readJson().then(fin, function(err) {
    console.log("err", err)
});

Summary of changes:

  1. Added resolve, reject arguments to Promise callback so we can use them
  2. Called reject(err) when there's an error
  3. Called resolve() when the async code is done.
  4. Passed a function reference for both .then() handlers.

FYI, when creating a promise wrapper around an async function, it is generally better to wrap just the function itself. This makes the wrapper 100% reusable and puts more of your code in the promise architecture which generally streamlines things and makes error handling easier. You could fix things up that way like this:

fs.readFilePromise = function(file, options) {
    return new Promise(function(resolve, reject) {
        fs.readFile(file, options, function(err, data) {
            if (err) return reject(err);
            resolve(data);
        });
    });
};

function readJson() {
    return fs.readFilePromise(__dirname + "/" + "bitSkin.json", 'utf8').then(function(data) {
        var bitCon = JSON.parse(data);
        bitCon.prices.forEach(function(item) {
            logger.info(item.market_hash_name);
        });
        return bitCon;
    });
}

Upvotes: 4

Related Questions