一个接一个地解决承诺(即按顺序)?

请考虑以下代码,它以串行/顺序方式读取文件数组。 返回一个 promise,只有在按顺序读取所有文件后,才会解析该 promise。readFiles

var readFile = function(file) {
  ... // Returns a promise.
};

var readFiles = function(files) {
  return new Promise((resolve, reject) => {
    var readSequential = function(index) {
      if (index >= files.length) {
        resolve();
      } else {
        readFile(files[index]).then(function() {
          readSequential(index + 1);
        }).catch(reject);
      }
    };

    readSequential(0); // Start with the first file!
  });
};

上面的代码有效,但我不喜欢必须对事情按顺序发生进行递归。有没有更简单的方法可以重写此代码,以便我不必使用我的奇怪函数?readSequential

最初我试图使用,但这导致所有调用同时发生,这不是我想要的:Promise.allreadFile

var readFiles = function(files) {
  return Promise.all(files.map(function(file) {
    return readFile(file);
  }));
};

答案 1

2017年更新:如果环境支持,我会使用异步函数:

async function readFiles(files) {
  for(const file of files) {
    await readFile(file);
  }
};

如果您愿意,可以使用异步生成器推迟读取文件,直到需要它们(如果您的环境支持):

async function* readFiles(files) {
  for(const file of files) {
    yield await readFile(file);
  }
};

更新:经过再三考虑 - 我可能会使用for循环代替:

var readFiles = function(files) {
  var p = Promise.resolve(); // Q() in q

  files.forEach(file =>
      p = p.then(() => readFile(file)); 
  );
  return p;
};

或者更紧凑地,减少:

var readFiles = function(files) {
  return files.reduce((p, file) => {
     return p.then(() => readFile(file));
  }, Promise.resolve()); // initial
};

在其他承诺库(如 when 和 Bluebird)中,您有用于此的实用程序方法。

例如,Bluebird将是:

var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs"));

var readAll = Promise.resolve(files).map(fs.readFileAsync,{concurrency: 1 });
// if the order matters, you can use Promise.each instead and omit concurrency param

readAll.then(function(allFileContents){
    // do stuff to read files.
});

尽管今天真的没有理由不使用异步等待。


答案 2

以下是我更喜欢串联运行任务的方式。

function runSerial() {
    var that = this;
    // task1 is a function that returns a promise (and immediately starts executing)
    // task2 is a function that returns a promise (and immediately starts executing)
    return Promise.resolve()
        .then(function() {
            return that.task1();
        })
        .then(function() {
            return that.task2();
        })
        .then(function() {
            console.log(" ---- done ----");
        });
}

任务较多的案例呢?比如,10?

function runSerial(tasks) {
  var result = Promise.resolve();
  tasks.forEach(task => {
    result = result.then(() => task());
  });
  return result;
}