如何让异步 readFile 方法遵循 node.js 中的顺序

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

我有这样的代码

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

结果将是

1
3
2

但我想要 1 2 3 我查看了文件系统的文档,它说 With the asynchronous methods there is no guaranteed ordering. 所以文档说如果我想要我的代码结果 1 2 3 它应该是

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

但是如果我正在构建一个有很多其他功能的系统需要在这段代码之后我们不能全部嵌套,因为这个函数需要在它被使用之后才能工作,所以它是短代码其他功能所以有没有办法让 fs.readFile 按顺序工作,但不包含在 fs 代码中需要在它之后工作的所有代码?

代码我有问题

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

使用async.waterfall,它可以让你运行按顺序执行异步函数,每个函数都依赖于前一个函数来完成。当最后一个函数完成时调用主回调,每个函数都可以将结果传递给下一个函数。 检查 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){

});

简单的解决方案是将要在 fs.readFile() 操作之后执行的其余代码放入函数中,然后将该函数插入异步回调中:

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

更现代的设计是对所有异步操作使用承诺,然后您可以更轻松地对事物进行排序。

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

当您有多个要排序的异步操作并且每个 returns 一个承诺时,使用承诺真正开始发挥优势的地方。然后,您可以这样做:

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

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

使用您为 urlex() 函数显示的代码,以下是您如何使用承诺完成所有工作:

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

这使用了几个不错的承诺功能,例如链接(对异步操作进行排序)。