Consider the following code that reads an array of files in a serial/sequential manner. readFiles
returns a promise, which is resolved only once all files have been read in sequence.
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! }); };
The above code works, but I don't like having to do recursion for things to occur sequentially. Is there a simpler way that this code can be re-written so that I don't have to use my weird readSequential
function?
Originally I tried to use Promise.all
, but that caused all of the readFile
calls to happen concurrently, which is not what I want:
var readFiles = function(files) { return Promise.all(files.map(function(file) { return readFile(file); })); };
You can just do Promise. resolve(files). each(fs. readFileAsync); which guarantees that promises are resolved sequentially in order.
"The point is that a promise will never resolve to another promise. It will always resolve to that promise's (eventual) value." Is jQuery $.
resolve() method "resolves" a given value to a Promise . If the value is a promise, that promise is returned; if the value is a thenable, Promise. resolve() will call the then() method with two callbacks it prepared; otherwise the returned promise will be fulfilled with the value.
Here, Promise. all() method is the order of the maintained promises. The first promise in the array will get resolved to the first element of the output array, the second promise will be a second element in the output array and so on.
Update 2017: I would use an async function if the environment supports it:
async function readFiles(files) { for(const file of files) { await readFile(file); } };
If you'd like, you can defer reading the files until you need them using an async generator (if your environment supports it):
async function* readFiles(files) { for(const file of files) { yield await readFile(file); } };
Update: In second thought - I might use a for loop instead:
var readFiles = function(files) { var p = Promise.resolve(); // Q() in q files.forEach(file => p = p.then(() => readFile(file)); ); return p; };
Or more compactly, with reduce:
var readFiles = function(files) { return files.reduce((p, file) => { return p.then(() => readFile(file)); }, Promise.resolve()); // initial };
In other promise libraries (like when and Bluebird) you have utility methods for this.
For example, Bluebird would be:
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. });
Although there is really no reason not to use async await today.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With