如何避免node.js的异步行为?

时间:2015-10-26 08:28:34

标签: javascript jquery node.js asynchronous

我上传多个文件并保存在某个目录中。我的代码如下

app.post('/file_upload', function (req, res) {
  var msgs = '';

  req.files.forEach(function(element) {
   var fileNameToWrite = __dirname + "\\uploads\\" + element.originalname;
   fs.readFile( element.path, function (err, data) {
        fs.writeFile(fileNameToWrite, data, function (err) {
         if( err ){
              msgs += element.originalname + " Uploaded Failed Error: '"+ err +"' ";
         }
         else{
              msgs += element.originalname + " Uploaded Successfully ";
          }
       });
   });
  },this);
  console.log("Final Msgs: " + msgs);
  res.end( JSON.stringify( msgs ) );
});

问题是 msgs 是异步填充的,我希望一旦forEach完成就得到msgs。我怎样才能做到这一点?

4 个答案:

答案 0 :(得分:1)

如果您真的想摆脱异步代码,可以使用writeFileSyncreadFileSync。但这不是一个好的实践。

一种简单的方法是使用这样的回调:

app.post('/file_upload', function (req, res) {
  var msgs = '';

  function finish() {
    console.log("Final Msgs: " + msgs);
    res.end( JSON.stringify( msgs ) );
  }

  var counter = 0;

  req.files.forEach(function(element) {
   var fileNameToWrite = __dirname + "\\uploads\\" + element.originalname;
   fs.readFile( element.path, function (err, data) {
        fs.writeFile(fileNameToWrite, data, function (err) {
         if( err ){
              msgs += element.originalname + " Uploaded Failed Error: '"+ err +"' ";
         }
         else{
              msgs += element.originalname + " Uploaded Successfully ";
          }

         // We call the finish when we write the last file
         counter += 1;
         if (counter == req.files.length) {
             finish();
         }
       });
   });
  },this);
});

答案 1 :(得分:1)

我希望使异步函数看起来不那么异步Promises。特别是Bluebird promise library非常好。从本质上讲,您的功能可能如下所示:

var Promise = require('bluebird')
var readFile = Promise.promisify(require('fs').readFile);

app.post('/file_upload', function (req, res) {
  var msgs = '';

  req.files.forEach(function(element) {
    var fileNameToWrite = __dirname + "\\uploads\\" + element.originalname;
    readFile(element.path).then(function (data) {
      return writeFile(filenameToWrite, data);
    }).then(function () {
      msgs += element.originalname + " Uploaded Successfully ";
    }).catch(function () {
      msgs += element.originalname + " Uploaded Failed Error: '"+ err +"' ";
    }).then(function () {
      console.log("Final Msgs: " + msgs);
      res.end( JSON.stringify( msgs ) );
    });
  },this);
});

这保持了异步调用的所有优点(例如,不锁定正在运行的线程),使您的API快速响应。尽管如此,如果只是通过链接then进行同步,你就可以编写代码“好像”。

答案 2 :(得分:1)

您可以使用多种方法,

1.使用writefilesync代替fs.writeFile,就像这样

req.files.forEach(function(element) {
  var fileNameToWrite = __dirname + "\\uploads\\" + element.originalname;

  try {
    fs.writeFileSync(fileNameToWrite, data);
    msgs += element.originalname + " Uploaded Successfully ";
  } catch(err) {
    msgs += element.originalname + " Uploaded Failed Error: '"+ err +"' ";
  }
}, this);

2.或者使用像asyncnpm i async --save-dev)这样的库,就像这样

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

async.eachSeries(req.files, function (element, next) {
  var fileNameToWrite = __dirname + "\\uploads\\" + element.originalname;

  fs.readFile(element.path, function (err, data) {
    if (err) {
      msgs += element.originalname + " Uploaded Failed Error: '"+ err +"' ";
    } else {
      msgs += element.originalname + " Uploaded Successfully ";
    }

    next();
  });  
}, function () {
  console.log("Final Msgs: " + msgs);
  res.end( JSON.stringify( msgs ) );
})

答案 3 :(得分:1)

@Nomi 我建议更改以下可以帮助您获得更多性能的内容。

1.将您的文件上传从单个帖子请求更改为多个帖子请求。我的意思是,您应该更改前端代码,以便向每个文件发送单独的发布请求。

2.在每个JavaScript请求中保存跟踪器/计数变量。基本上,计数器值最初与文件数相同。然后在每个文件完全上传时减少1。当计数器值达到0时,您可以显示成功消息。可能您可以使用promise进行此文件跟踪。

3.修改服务器端代码,以流模式处理文件上传请求。您可能希望看到以下服务器端代码。

var fs = require('fs');
app.post('/file_upload', function(req, res) {
  // Read file name and extension from request header and replace 'somefile.someExtension' below.
  var fileNameToWrite = __dirname + "\\uploads\\" + 'somefile.someExtension';
  var wStream = fs.createWriteStream(fileNameToWrite);
  req.pipe(wStream);
});

希望这会给你一个不同的想法。