等待许多异步函数执行

时间:2017-07-25 04:30:42

标签: javascript node.js asynchronous promise

我有promise函数,可以在循环中执行几次不同数据的异步函数。我想等到所有异步函数都被执行然后解析(),(或在非承诺函数中调用回调函数)

var readFiles = ()=>{
  return new Promise((resolve,reject)=>{
    var iterator = 0;
    var contents = {};
    for(let i in this.files){
      iterator++;
      let p = path.resolve(this.componentPath,this.files[i]);
      fs.readFile(p,{encoding:'utf8'},(err,data)=>{
        if(err){
          reject(`Could not read ${this.files[i]} file.`);
        } else {
          contents[this.files[i]] = data;
          iterator--;
          if(!iterator) resolve(contents);
        }
      });
    }
    if(!iterator) resolve(contents); //in case of !this.files.length
  });
};

我在每次循环重复时增加iterator,然后在async函数的回调减少iterator中检查是否所有异步函数都已完成(iterator === 0),如果是 - 致电resolve()

效果很好,但似乎不优雅和可读。你知道更好的解决这个问题的方法吗?

3 个答案:

答案 0 :(得分:2)

使用一些代码和更多细节跟进评论!

Promise.all()接受迭代器,并等待所有承诺解析或拒绝。然后它将返回所有承诺的结果。因此,我们可以创建很少的承诺并将它们添加到数组中,而不是跟踪所有承诺何时解决。然后,使用Promise.all()等待所有这些解析。

const readFiles = () => {
  const promises = [];

  for(let i in files) {
    const p = path.resolve(componentPath, files[i]);

    promises.push(new Promise((resolve, reject) => {
      fs.readFile(p, {encoding:'utf8'}, (err, data) => {
        if(err) {
          reject(`Could not read ${files[i]} file.`);
        } else {
          resolve(data);
        }
      });
    }));
  }

  return Promise.all(promises);
};

const fileContents = readFiles().then(contents => {
    console.log(contents)
})
.catch(err => console.error(err));

答案 1 :(得分:1)

您只需将所有Promises推送到数组中,然后将其作为参数传递给Promise.all(arrayOfPromises)

尝试这样的事情:

var readFiles = () => {
  var promises = [];
  let contents = {};
  var keys_files = Object.keys(this.files);
  if (keys_files.length <= 0) {
   var promise = new Promise((resolve, reject) => {
     resolve(contents);
   });
   promises.push(promise);
  }

  keys_files.forEach((key) => {
    var file = this.files[key];
    var promise = new Promise((resolve, reject) => {
       const currentPath = path.resolve(this.componentPath, file);
       fs.readFile(p,{encoding:'utf8'},(err, data) => {
         if (err) {
            return reject(`Could not read ${file} file.`);
         }

         contents[file] = data;
         resolve(contents)
       });
    });
  });

  return Promises.all(promises);
}

然后你应该使用这样的函数:

// this will return a promise that contains an array of promises
var readAllFiles = readFiles();
// the then block only will execute if all promises were resolved if one of them were reject so all the process was rejected automatically
readAllFiles.then((promises) => {
   promises.forEach((respond) => {
      console.log(respond);
   });
}).catch((error) => error);

如果您不在乎其中一项承诺被拒绝,也许您应该执行以下操作

var readFiles = () => {
  var promises = [];
  let contents = {};
  var keys_files = Object.keys(this.files);
  if (keys_files.length <= 0) {
   var promise = new Promise((resolve, reject) => {
     resolve(contents);
   });
   promises.push(promise);
  }

  keys_files.forEach((key) => {
    var file = this.files[key];
    var promise = new Promise((resolve, reject) => {
       const currentPath = path.resolve(this.componentPath, file);
       fs.readFile(p,{encoding:'utf8'},(err, data) => {
         // create an object with the information
         let info = { completed: true };
         if (err) {
            info.completed = false;
            info.error = err;
            return resolve(info);
         }

         info.data = data;
         contents[file] = info;
         resolve(contents)
       });
    });
  });

  return Promises.all(promises);
}

答案 2 :(得分:1)

复制评论:

  

此外 - 您可能希望使用fs-extrafs的替代品,但添加了承诺支持。

这是怎么回事:

const fs = require('fs-extra');
var readFiles = ()=>{
  let promises = files
    .map(file => path.resolve(componentPath, file))
    .map(path => fs.readFile(path));
  return Promise.all(promises);
});

干净整洁。然后,您可以获得以下内容:

readFiles()
.then(contents => { ... })
.catch(error => { ... });

这会在第一次出错时失败(因为那是Promise.all所做的事情)。如果您需要单独的错误处理,可以添加另一个map行:

.map(promise => promise.catch(err => err));

然后你可以过滤结果:

let errors = contents.filter(content => content instanceof Error)
let successes = contents.filter(content => !(content instanceof Error))