Andrew Young
Andrew Young

Reputation: 55

How to let asynchronous readFile method follow order in node.js

I have a code like this

var fs = require('fs');
console.log("1");
fs.readFile("./index.js", function(err, data) {
    if (err) {
         console.log("err");
    }
    console.log("2");
});
console.log("3");

and the result will be

1
3
2

But I want 1 2 3 I have look through filesystem's document and it says With the asynchronous methods there is no guaranteed ordering. so the document says if I want my code result 1 2 3 it should be

var fs = require('fs');
console.log("1");
fs.readFile("./index.js", function(err, data) {
    if (err) {
         console.log("err");
    }
    console.log("2");
    console.log("3");
});

But it's for short code if I'm building a system which has a lot of other function needs to be after this code we can't nested all in because the function need to be work after it will be use by other functions so is there a way to let the fs.readFile work in order but not incuding all the code need to work after it in fs code?

code I have issue

function servedatastart() {

    if (urlcontentbasic(urlslug) != "err") {
        responsearray["data"] = urlcontentbasic(urlslug);
    } else {
        // callback function
        console.log("urlex");
        urlex("none", urlslug); // this function I use fs.readFile(urlslug, function...
    }
    console.log("other function");
    //other function
    cuswriteheader();
    includescript();
    writefooter()
    res.end();   
}

Upvotes: 1

Views: 1080

Answers (2)

jfriend00
jfriend00

Reputation: 707278

The simple solution is to just put the rest of your code that you want to execute after the fs.readFile() operation into a function and then insert that function into the async callback:

var fs = require('fs');
console.log("1");
fs.readFile("./index.js", function(err, data) {
    if (err) {
         console.log("err");
    }
    console.log("2");
    moreCode();
});

function moreCode() {
    console.log("3");
}

A bit more modern design is to use promises for all your asynchronous operations and then you can sequence things a bit easier.

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));

console.log("1");
fs.readFileAsync("./index.js").then((data) => {
    console.log("2");
    return moreCode();
}).catch((err) => {
    // handle error here
});


function moreCode() {
    console.log("3");
}

Where the benefit of using promises really starts to take advantage is when you have multiple asynchronous operations that you want to sequence and each one returns a promise. Then, you can do that like this:

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));

fs.readFileAsync("./index.js")
  .then(moreCode1)
  .then(moreCode2)
  .catch((err) => {
      // handle error here
  });

Using the code you've show for the urlex() function, here's how you can make all that work using promises:

// include infrastructure to automatically promisify the fs module
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));

function servedatastart() {

    Promise.resolve().then(function() {
        if (urlcontentbasic(urlslug) != "err") {
            // I'm assuming that urlcontentbasic is also async and returns a promise
            // like urlexbasic does
            return urlcontentbasic(urlslug);
        } else {
            // callback function
            console.log("urlex");
            return urlex("none", urlslug); 
        }
    }).then(function() {
        console.log("other function");
        //other function
        cuswriteheader();
        includescript();
        writefooter()
        res.end();   
    });
}


// basic function for reading extension-less URL
// returns a promise whose resolved value is a returnarray data structure
function urlexbasic(filetype, slug) {
    var returnarray = {status:null,contenttype:null,data:null,message:undefined};
    if (slug == undefined) {
        slug = "";
    }

    if (filetype == "none") {
        var filename;
        console.log("path: "+pathdataarray["1"]);
        if (pathdataarray["1"] == "/pre") {
            console.log("substr");
            filename = urlslug.substr(4, urlslug.length-5);
            console.log("1: "+filename);
        }
        urldata(q.pathname, "");
        console.log("slug: "+ slug+" filename: "+ filename);
        if (slug != "" || filename != undefined) {
            console.log("what?")
            if (filename != undefined) {
                filename = "." + filename;
            } else {
                filename = "." + slug;
            }
            console.log("2: "+filename);
        } else {
            filename = "." + urlslug.substr(0, urlslug.length-1);
        }
        console.log("filename: "+ filename);
        return fs.readFileAsync(filename.substr(0, filename.length-1), 'utf8').then(function(data) {
            console.log("readfile");
            var mime = null;
            if (urldataarray.extension == "js") {
                mime = "javascript";
            } else {
                mime = urldataarray.extension;
            }
            console.log("hey running");
            returnarray["status"] = 200;
            returnarray["contenttype"] = 'text/'+mime;
            //res.writeHead(200, {'contenttype': 'text/'+mime});
            returnarray["data"] = data;
            console.log("instatus: "+returnarray["status"]);
            // make returnarray be the resolved value of the promise
            return returnarray;
        }).catch(function(err) {
            console.log("404 error");
            returnarray["status"] = 404;
        });
    } else {
        urldata(q.pathname, filetype);
        var filename;
        if (pathdataarray["1"] == "/pre") {
            console.log("substr");
            filename = urlslug.substr(4, urlslug.length-5);
            console.log("11: "+filename);
        }
        console.log("2slug: "+ slug+" filename: "+ filename);
        if (slug != "" || filename != undefined) {
            if (filename) {
                filename = "." + filename + "." + filetype;
            } else {
                filename = "." + slug +"."+ filetype;
            }
            console.log("22: "+filename);
        } else {
            filename = "." + urlslug.substr(0, urlslug.length-1) + "." + filetype;
        }
        return fs.readFileAsync(filename, 'utf8').then(function(data) {
            var mime = null;
            if (urldataarray.extension == "js") {
                mime = "javascript";
            } else {
                mime = urldataarray.extension;
                console.log("ok");
            };

            if (pathdataarray["1"] == "/pre") {
                returnarray["status"] = 200;
                returnarray["contenttype"] = 'text/plain';
                //res.writeHead(200, {'contenttype': 'text/plain'});
                //res.write("<pre><code>");
                returnarray["data"] = data;
                //res.write("</code></pre>");
            } else {
                returnarray["status"] = 200;
                returnarray["contenttype"] = 'text/'+mime;
                //res.writeHead(200, {'contenttype': 'text/'+mime});
                console.log("pure data");
                returnarray["data"] = data;
            }
            // make returnarray be the resolved value of the promise
            return returnArray;
        }).catch(function(err) {
            return urlex(dataerrcall[filetype]);
        });
    }
}

// returns a promise whose resolved value is a returnArray 
function urlex(filetypeex, slugex) {
    //console.log(urlexbasic(filetypeex, slugex));
    return urlexbasic(filetypeex, slugex).then(function(returnArray) {
        console.log("return: "+returnarray.status);
        responsearray["status"] = returnarray["status"];

        responsearray["contenttype"] = returnarray["contenttype"]
        responsearray["message"] = returnarray["message"];
        responsearray["data"] = returnarray["data"];
        return responsearray;
    });
}

This uses several nice promise features such as chaining (to sequence async operations).

Upvotes: 1

Lena Kaplan
Lena Kaplan

Reputation: 756

Use async.waterfall, it allows you to run asynchronous functions in order, each function depends on the previous one to complete. when the last function completes the main callback is called, each function can pass the result to the next one. check https://caolan.github.io/async/docs.html

var fs = require('fs');
var async = require('async');

async.waterfall([
    function (callback) {
        console.log("1");
        callback();
    },
    function (arg1, callback) {
        fs.readFile("./index.js", callback);
        console.log("2");
    },
    function (arg2, callback) {
        console.log("3");
        callback()
    }
], function(err, res){

});

Upvotes: 4

Related Questions