考虑以下以串行/顺序方式读取文件数组的代码。readFiles返回一个承诺,只有在所有文件都按顺序读取后才会解决。
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函数了?
readSequential
最初我尝试使用Promise.all,但这导致所有readFile调用同时发生,这 不是 我想要的:
Promise.all
readFile
var readFiles = function(files) { return Promise.all(files.map(function(file) { return readFile(file); })); };
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 };
在其他 Promise 库(如 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。