简体   繁体   English

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

[英]How to avoid async behviour of node.js?

I am uploading multiple files and saving in some directory. 我正在上传多个文件并保存在某个目录中。 My code is as below 我的代码如下

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

The problem is msgs is populated asynchronously, I want to get msgs once forEach is complete. 问题是msgs异步填充,一旦forEach完成,我想获取msgs。 How can I achieve this? 我该如何实现?

If you really want to get rid of the asynchronous code, you can use writeFileSync and readFileSync . 如果您确实想摆脱异步代码,则可以使用writeFileSyncreadFileSync But it is not a good pratice. 但这不是一个好习惯。

A simple way is to use a callback like this : 一种简单的方法是使用这样的回调:

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

What I would go for to make the asynchronous functions not seem so asynchronous are Promises . 我希望使异步函数看起来不那么异步的是Promises In particular the Bluebird promise library is excellent. 尤其是Bluebird Promise库非常出色。 In essence, your function could look like this: 本质上,您的函数可能如下所示:

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

This keeps all the goodiness of asynchronous calls (eg not locking the running thread), making your API quick and responsive. 这样可以保持异步调用的所有优势(例如,不锁定运行线程),从而使您的API快速响应。 Still, it let's you write code "as if" if was synchronous by just chaining then . 不过,如果仅通过链接then同步,它仍可以让您编写代码“好像”。

You can use several approaches, 您可以使用几种方法,

1.Use writefilesync instead of fs.writeFile , like this 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.Or use libraries like async ( npm i async --save-dev ), like so 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 ) );
})

@Nomi I would recommend to change following things which will help you to gain much performance. @Nomi我建议更改以下内容,这将帮助您获得很多性能。

1.Change your file upload from single post request to multiple post requests. 1.将文件上传从单个发布请求更改为多个发布请求。 I meant, you should change your front-end code to send separate post request to each file. 我的意思是,您应该更改前端代码以将单独的后期请求发送到每个文件。

2.Keep a tracker/count variable in each JavaScript request. 2.在每个JavaScript请求中保留跟踪器/计数变量。 Basically, the counter value initially same as number of files. 基本上,计数器值最初与文件数相同。 Then reduce by 1, when each file fully uploaded. 每个文件完全上传后,再减少1。 And you can show the success message, when the counter value reaches 0. Probably, you can use promise for this file tracking. 并且当计数器值达到0时,您可以显示成功消息。可能的是,您可以使用promise进行此文件跟踪。

3.Modify your server-side code to handle file upload request in streaming mode. 3.修改您的服务器端代码,以流式处理文件上传请求。 You may want to see the following server-side code. 您可能需要查看以下服务器端代码。

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

Hopefully, this will give you an alternate idea. 希望这会给您一个替代的主意。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM