考虑下面以串行/顺序方式读取文件数组的代码。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。但是这会导致所有的readFile调用并发发生,这不是我想要的:

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

当前回答

另外的例子

const addTwo = async () => 2; const addThree = async (inValue) => new Promise((resolve) => setTimeout(resolve(inValue + 3), 2000)); const addFour = (inValue) => new Promise((res) => setTimeout(res(inValue + 4), 1000)); const addFive = async (inValue) => inValue + 5; // Function which handles promises from above async function sequenceAddition() { let sum = await [addTwo, addThree, addFour, addFive].reduce( (promise, currPromise) => promise.then((val) => currPromise(val)), Promise.resolve() ); console.log('sum:', sum); // 2 + 3 + 4 + 5 = 14 } // Run function. See console for result. sequenceAddition();

使用reduce()的通用语法

function sequence(tasks, fn) {
    return tasks.reduce((promise, task) => promise.then(() => fn(task)), Promise.resolve());
}

更新

items-promise是一个准备使用NPM包做同样的事情。

其他回答

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

或者更确切地说,用reduce:

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

在其他承诺库(如when和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.
});

尽管现在没有理由不使用async await。

我的答案基于https://stackoverflow.com/a/31070150/7542429。

Promise.series = function series(arrayOfPromises) {
    var results = [];
    return arrayOfPromises.reduce(function(seriesPromise, promise) {
      return seriesPromise.then(function() {
        return promise
        .then(function(result) {
          results.push(result);
        });
      });
    }, Promise.resolve())
    .then(function() {
      return results;
    });
  };

该解决方案以Promise.all()等数组的形式返回结果。

用法:

Promise.series([array of promises])
.then(function(results) { 
  // do stuff with results here
});

正如Bergi所注意到的,我认为最好的和明确的解决方案是使用蓝鸟。每个,代码如下:

const BlueBird = require('bluebird');
BlueBird.each(files, fs.readFileAsync);

我发现自己多次回到这个问题上,答案并没有给我我想要的,所以把这个放在这里给任何需要这个问题的人。

下面的代码执行顺序承诺执行(一个接一个),每一轮由多个调用组成:

async function sequence(list, cb) {
  const result = [];
  await list.reduce(async (promise, item) => promise
    .then(() => cb(item))
    .then((res) => result.push(res)
  ), Promise.resolve());
  return result;
}

展示:

<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.15.3/axios.min.js"></script> <script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script> <script type="text/babel"> function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function readFile(url, index) { console.log('Running index: ', index); // First action const firstTime = await axios.get(url); console.log('First API response: ', firstTime.data.activity); // Second action await sleep(1000); // Third action const secondTime = await axios.get(url); console.log('Second API response: ', secondTime.data.activity); // Fourth action await sleep(1000); return secondTime.data; } async function sequence(urls, fn) { const result = []; await urls.reduce(async (promise, url, index) => promise.then(() => fn(url, index)).then((res) => result.push(res)), Promise.resolve()); return result; } const urls = [ 'https://www.boredapi.com/api/activity', 'https://www.boredapi.com/api/activity', 'https://www.boredapi.com/api/activity', ]; (async function init() { const result = await sequence(urls, readFile); console.log('result', result); })() </script>

我使用以下代码扩展Promise对象。它处理承诺的拒绝并返回一个结果数组

Code

/*
    Runs tasks in sequence and resolves a promise upon finish

    tasks: an array of functions that return a promise upon call.
    parameters: an array of arrays corresponding to the parameters to be passed on each function call.
    context: Object to use as context to call each function. (The 'this' keyword that may be used inside the function definition)
*/
Promise.sequence = function(tasks, parameters = [], context = null) {
    return new Promise((resolve, reject)=>{

        var nextTask = tasks.splice(0,1)[0].apply(context, parameters[0]); //Dequeue and call the first task
        var output = new Array(tasks.length + 1);
        var errorFlag = false;

        tasks.forEach((task, index) => {
            nextTask = nextTask.then(r => {
                output[index] = r;
                return task.apply(context, parameters[index+1]);
            }, e=>{
                output[index] = e;
                errorFlag = true;
                return task.apply(context, parameters[index+1]);
            });
        });

        // Last task
        nextTask.then(r=>{
            output[output.length - 1] = r;
            if (errorFlag) reject(output); else resolve(output);
        })
        .catch(e=>{
            output[output.length - 1] = e;
            reject(output);
        });
    });
};

例子

function functionThatReturnsAPromise(n) {
    return new Promise((resolve, reject)=>{
        //Emulating real life delays, like a web request
        setTimeout(()=>{
            resolve(n);
        }, 1000);
    });
}

var arrayOfArguments = [['a'],['b'],['c'],['d']];
var arrayOfFunctions = (new Array(4)).fill(functionThatReturnsAPromise);


Promise.sequence(arrayOfFunctions, arrayOfArguments)
.then(console.log)
.catch(console.error);