Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolve promises one after another (i.e. in sequence)?

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);   })); }; 
like image 486
XåpplI'-I0llwlg'I - Avatar asked Jul 05 '14 11:07

XåpplI'-I0llwlg'I -


People also ask

How do you resolve promises sequentially?

You can just do Promise. resolve(files). each(fs. readFileAsync); which guarantees that promises are resolved sequentially in order.

Can a promise resolve another promise?

"The point is that a promise will never resolve to another promise. It will always resolve to that promise's (eventual) value." Is jQuery $.

What does resolve () do in a promise?

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.

Does promise all resolve promises in order?

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.


1 Answers

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.

like image 169
Benjamin Gruenbaum Avatar answered Sep 25 '22 01:09

Benjamin Gruenbaum