web-dev-qa-db-fra.com

Résoudre les promesses les unes après les autres (c’est-à-dire en séquence)?

Considérez le code suivant qui lit un tableau de fichiers de manière série/séquentielle. readFiles renvoie une promesse, qui n'est résolue que lorsque tous les fichiers ont été lus dans l'ordre.

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!

  });
};

Le code ci-dessus fonctionne, mais je n'aime pas avoir à faire de la récursivité pour que les choses se produisent de manière séquentielle. Existe-t-il un moyen plus simple de réécrire ce code afin de ne pas utiliser ma fonction bizarre readSequential?

A l'origine, j'ai essayé d'utiliser Promise.all, mais cela a provoqué tous les appels readFile en même temps, ce qui est pas ce que je veux:

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

Update 2017 : J'utiliserais une fonction asynchrone si l'environnement le permet:

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

Si vous le souhaitez, vous pouvez différer la lecture des fichiers jusqu'à ce que vous en ayez besoin à l'aide d'un générateur async (si votre environnement le prend en charge):

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

Mise à jour: À bien y penser, je pourrais utiliser une boucle for à la place:

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

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

Ou plus compactement, avec réduire:

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

Dans d'autres bibliothèques de promesses (comme When et Bluebird), vous disposez de méthodes utilitaires pour cela.

Par exemple, Bluebird serait:

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

Bien qu'il n'y ait vraiment aucune raison not d'utiliser l'attente asynchrone aujourd'hui.

220
Benjamin Gruenbaum

Voici comment je préfère exécuter des tâches en série .

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

Qu'en est-il des cas avec plus de tâches? Comme, 10?

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

Cette question est ancienne, mais nous vivons dans un monde d'ES6 et de JavaScript fonctionnel, voyons comment nous pouvons nous améliorer.

Comme les promesses sont exécutées immédiatement, nous ne pouvons pas simplement créer un tableau de promesses, elles se déclencheraient toutes en parallèle.

Au lieu de cela, nous devons créer un tableau de fonctions qui renvoie une promesse. Chaque fonction sera ensuite exécutée séquentiellement, ce qui lancera la promesse à l'intérieur.

Nous pouvons résoudre ce problème de plusieurs manières, mais ma méthode préférée consiste à utiliser reduce.

Cela devient un peu délicat d’utiliser reduce en combinaison avec des promesses, c’est pourquoi j’ai décomposé la doublure en plusieurs petites bouchées digestibles ci-dessous.

L'essence de cette fonction consiste à utiliser reduce à partir d'une valeur initiale de Promise.resolve([]) ou d'une promesse contenant un tableau vide.

Cette promesse sera ensuite transmise à la méthode reduce en tant que promise. C’est la clé pour enchaîner chaque promesse les unes après les autres. La prochaine promesse à exécuter est func et lorsque la then est déclenchée, les résultats sont concaténés et cette promesse est ensuite renvoyée, en exécutant le cycle reduce avec la fonction promesse suivante.

Une fois que toutes les promesses ont été exécutées, la promesse retournée contiendra un tableau de tous les résultats de chaque promesse.

ES6 Exemple (une doublure)

/*
 * serial executes Promises sequentially.
 * @param {funcs} An array of funcs that return promises.
 * @example
 * const urls = ['/url1', '/url2', '/url3']
 * serial(urls.map(url => () => $.ajax(url)))
 *     .then(console.log.bind(console))
 */
const serial = funcs =>
    funcs.reduce((promise, func) =>
        promise.then(result => func().then(Array.prototype.concat.bind(result))), Promise.resolve([]))

ES6 Exemple (décomposé)

// broken down to for easier understanding

const concat = list => Array.prototype.concat.bind(list)
const promiseConcat = f => x => f().then(concat(x))
const promiseReduce = (acc, x) => acc.then(promiseConcat(x))
/*
 * serial executes Promises sequentially.
 * @param {funcs} An array of funcs that return promises.
 * @example
 * const urls = ['/url1', '/url2', '/url3']
 * serial(urls.map(url => () => $.ajax(url)))
 *     .then(console.log.bind(console))
 */
const serial = funcs => funcs.reduce(promiseReduce, Promise.resolve([]))

Usage:

// first take your work
const urls = ['/url1', '/url2', '/url3', '/url4']

// next convert each item to a function that returns a promise
const funcs = urls.map(url => () => $.ajax(url))

// execute them serially
serial(funcs)
    .then(console.log.bind(console))
48
joelnet

Pour le faire simplement dans ES6:

function(files) {

    // Create a new empty promise (don't do that with real people ;)
    var sequence = Promise.resolve();

    // Loop over each file, and add on a promise to the
    // end of the 'sequence' promise.
    files.forEach(function(file) {

      // Chain one computation onto the sequence
      sequence = sequence.then(function() {
        return performComputation(file);
      }).then(function(result) {
        doSomething(result) // Resolves for each file, one at a time.
      });

    })

    // This will resolve after the entire chain is resolved
    return sequence;
  }
34
Shridhar Gupta

Utilisation simple pour la promesse standard de Node.js:

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

METTRE À JOUR

items-promise est un package NPM prêt à l'emploi faisant de même.

23
Pooya

J'ai dû exécuter beaucoup de tâches séquentielles et j'ai utilisé ces réponses pour créer une fonction permettant de gérer toute tâche séquentielle ...

function one_by_one(objects_array, iterator, callback) {
    var start_promise = objects_array.reduce(function (prom, object) {
        return prom.then(function () {
            return iterator(object);
        });
    }, Promise.resolve()); // initial
    if(callback){
        start_promise.then(callback);
    }else{
        return start_promise;
    }
}

La fonction prend 2 arguments + 1 optionnel. Le premier argument est le tableau sur lequel nous allons travailler. Le deuxième argument est la tâche elle-même, une fonction qui renvoie une promesse, la tâche suivante ne sera lancée que lorsque cette promesse sera résolue. Le troisième argument est un rappel à exécuter lorsque toutes les tâches ont été effectuées. Si aucun rappel n'est passé, la fonction retourne la promesse créée afin que nous puissions gérer la fin.

Voici un exemple d'utilisation:

var filenames = ['1.jpg','2.jpg','3.jpg'];
var resize_task = function(filename){
    //return promise of async resizing with filename
};
one_by_one(filenames,resize_task );

J'espère que ça fait gagner du temps à quelqu'un ...

11
Salketer

Ceci est une légère variation d'une autre réponse ci-dessus. Utilisation de promesses natives:

function inSequence(tasks) {
    return tasks.reduce((p, task) => p.then(task), Promise.resolve())
}

Explication

Si vous avez ces tâches [t1, t2, t3], alors ce qui précède équivaut à Promise.resolve().then(t1).then(t2).then(t3). C'est le comportement de réduire.

Comment utiliser

First Vous devez construire une liste de tâches! Une tâche est une fonction qui n'accepte aucun argument. Si vous devez passer des arguments à votre fonction, utilisez bind ou d'autres méthodes pour créer une tâche. Par exemple:

var tasks = files.map(file => processFile.bind(null, file))
inSequence(tasks).then(...)
4
Hai Phan

Ma solution préférée:

function processArray(arr, fn) {
    return arr.reduce(
        (p, v) => p.then((a) => fn(v).then(r => a.concat([r]))),
        Promise.resolve([])
    );
}

Ce n'est pas fondamentalement différent des autres publiés ici, mais:

  • Applique la fonction aux éléments en série
  • Résout à un tableau de résultats
  • Ne nécessite pas asynchrone/wait (le support est encore assez limité, vers 2017)
  • Utilise les fonctions de flèche; Gentil et concis

Exemple d'utilisation:

const numbers = [0, 4, 20, 100];
const multiplyBy3 = (x) => new Promise(res => res(x * 3));

// Prints [ 0, 12, 60, 300 ]
processArray(numbers, multiplyBy3).then(console.log);

Testé sur les moteurs Chrome (v59) et NodeJS (v8.1.2).

4
Molomby

La meilleure solution que j'ai pu trouver était avec des promesses bluebird. Vous pouvez simplement faire Promise.resolve(files).each(fs.readFileAsync); qui garantit que les promesses sont résolues de manière séquentielle dans l'ordre.

4
Mikael Lepistö

Utilisez Array.prototype.reduce et n'oubliez pas de mettre vos promesses dans une fonction, sinon elles seront déjà exécutées!

// array of Promise providers

const providers = [
  function(){
     return Promise.resolve(1);
  },
  function(){
     return Promise.resolve(2);
  },
  function(){
     return Promise.resolve(3);
  }
]


const seed = Promise.resolve(null);

const inSeries = function(providers){
  return providers.reduce(function(a,b){
      return a.then(b);
  }, seed);
};

Sympa et facile .... vous devriez pouvoir réutiliser la même graine pour la performance, etc.

2
Alexander Mills

J'ai créé cette méthode simple sur l'objet Promise:

Créer et ajouter une méthode Promise.sequence à l'objet Promise

Promise.sequence = function (chain) {
    var results = [];
    var entries = chain;
    if (entries.entries) entries = entries.entries();
    return new Promise(function (yes, no) {
        var next = function () {
            var entry = entries.next();
            if(entry.done) yes(results);
            else {
                results.Push(entry.value[1]().then(next, function() { no(results); } ));
            }
        };
        next();
    });
};

Usage:

var todo = [];

todo.Push(firstPromise);
if (someCriterium) todo.Push(optionalPromise);
todo.Push(lastPromise);

// Invoking them
Promise.sequence(todo)
    .then(function(results) {}, function(results) {});

La meilleure chose à propos de cette extension à l'objet Promise est qu'elle est cohérente avec le style des promesses. Promise.all et Promise.sequence sont invoquées de la même manière, mais ont une sémantique différente.

Mise en garde

L'exécution séquentielle des promesses n'est généralement pas un très bon moyen de les utiliser. Il est généralement préférable d’utiliser Promise.all et de laisser le navigateur exécuter le code aussi rapidement que possible. Cependant, il existe de réels cas d'utilisation - par exemple, lors de l'écriture d'une application mobile à l'aide de javascript.

2
frodeborli

J'ai bien aimé la réponse de @ joelnet, mais pour moi, ce style de codage est un peu difficile à digérer. J'ai donc passé quelques jours à essayer de comprendre comment exprimer la même solution de manière plus lisible. prendre, juste avec une syntaxe différente et quelques commentaires.

// first take your work
const urls = ['/url1', '/url2', '/url3', '/url4']

// next convert each item to a function that returns a promise
const functions = urls.map((url) => {
  // For every url we return a new function
  return () => {
    return new Promise((resolve) => {
      // random wait in milliseconds
      const randomWait = parseInt((Math.random() * 1000),10)
      console.log('waiting to resolve in ms', randomWait)
      setTimeout(()=>resolve({randomWait, url}),randomWait)
    })
  }
})


const promiseReduce = (acc, next) => {
  // we wait for the accumulator to resolve it's promise
  return acc.then((accResult) => {
    // and then we return a new promise that will become
    // the new value for the accumulator
    return next().then((nextResult) => {
      // that eventually will resolve to a new array containing
      // the value of the two promises
      return accResult.concat(nextResult)
    })
  })
};
// the accumulator will always be a promise that resolves to an array
const accumulator = Promise.resolve([])

// we call reduce with the reduce function and the accumulator initial value
functions.reduce(promiseReduce, accumulator)
  .then((result) => {
    // let's display the final value here
    console.log('=== The final result ===')
    console.log(result)
  })
2
Gabriel Acosta

Vous pouvez utiliser cette fonction qui obtient promiseFactories List:

function executeSequentially(promiseFactories) {
    var result = Promise.resolve();
    promiseFactories.forEach(function (promiseFactory) {
        result = result.then(promiseFactory);
    });
    return result;
}

Promise Factory est une simple fonction qui renvoie une promesse:

function myPromiseFactory() {
    return somethingThatCreatesAPromise();
}

Cela fonctionne car une fabrique de promesses ne crée pas la promesse tant qu'elle n'est pas sollicitée. Cela fonctionne de la même manière qu'une fonction d'alors - en fait, c'est la même chose!

Vous ne voulez pas accomplir une foule de promesses. Selon les spécifications de la promesse, dès qu'une promesse est créée, elle commence à être exécutée. Donc, ce que vous voulez vraiment, c'est un ensemble d'usines de promesses ...

Si vous voulez en savoir plus sur Promises, vous devriez vérifier ce lien: https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html

2
sidanmor

J'utilise le code suivant pour étendre l'objet Promise. Il gère le rejet des promesses et renvoie un tableau de résultats 

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

Exemple

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);
1
josemontesp

Si vous le souhaitez, vous pouvez utiliser réduire pour faire une promesse séquentielle, par exemple:

[2,3,4,5,6,7,8,9].reduce((promises, page) => {
    return promises.then((page) => {
        console.log(page);
        return Promise.resolve(page+1);
    });
  }, Promise.resolve(1));

ça marche toujours en séquentiel.

1
Victor Palomo

Comme Bergi l’a remarqué, je pense que la meilleure solution, c’est d’utiliser BlueBird.each, code ci-dessous:

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

Ma réponse basée sur 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;
    });
  };

Cette solution renvoie les résultats sous forme de tableau comme Promise.all ().

Usage:

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

La méthode Array Push and Pop peut être utilisée pour une séquence de promesses. Vous pouvez également envoyer de nouvelles promesses lorsque vous avez besoin de données supplémentaires. C’est le code que je vais utiliser dans le chargeur React Infinite pour charger une séquence de pages.

var promises = [Promise.resolve()];

function methodThatReturnsAPromise(page) {
        return new Promise((resolve, reject) => {
                setTimeout(() => {
                        console.log(`Resolve-${page}! ${new Date()} `);
                        resolve();
                }, 1000);
        });
}

function pushPromise(page) {
        promises.Push(promises.pop().then(function () {
                return methodThatReturnsAPromise(page)
        }));
}

pushPromise(1);
pushPromise(2);
pushPromise(3);
0
Muthu Kumar

Je ne comprends pas pourquoi les gens proposent des solutions aussi complexes. Voici une logique plus simple

Avec Async/Await - La meilleure solution si vous avez le support de ES7

function downloadFile(fileUrl) { ... } // This function return a Promise

async function main()
{
  var filesList = [...];
  for (var i = 0; i <= filesList.length; i++)
  {
    await downloadFile(filesList[i]);
  }
}

Sans Async/Attendre

function downloadFile(fileUrl) { ... } // This function return a Promise

function downloadRecursion(filesList, index)
{
  if (index < filesList.length)
  {
    downloadFile(filesList[index]).then(function()
    {
      index++;
      downloadRecursion(filesList, index); // self invocation - recursion!
    });
  }
  else
  {
    return Promise.resolve();
  }
}

function main()
{
  var filesList = [...];
  downloadFile(filesList, 0);
}
0
Gil Epshtain

Si quelqu'un d'autre a besoin d'un moyen garanti de manière STRICTEMENT séquentielle de résoudre les promesses lors de l'exécution d'opérations CRUD, vous pouvez également utiliser le code suivant comme base.

Tant que vous ajoutez 'return' avant d'appeler chaque fonction, décrivant une promesse, et utilisez cet exemple comme base, l'appel de fonction suivant .then () commencera CONSISTANTEMENT après l'achèvement de la précédente:

getRidOfOlderShoutsPromise = () => {
    return readShoutsPromise('BEFORE')
    .then(() => {
        return deleteOlderShoutsPromise();
    })
    .then(() => {
        return readShoutsPromise('AFTER')
    })
    .catch(err => console.log(err.message));
}

deleteOlderShoutsPromise = () => {
    return new Promise ( (resolve, reject) => {
        console.log("in deleteOlderShouts");
        let d = new Date();
        let TwoMinuteAgo = d - 1000 * 90 ;
        All_Shouts.deleteMany({ dateTime: {$lt: TwoMinuteAgo}}, function(err) {
            if (err) reject();
            console.log("DELETED OLDs at "+d);
            resolve();        
        });
    });
}

readShoutsPromise = (tex) => {
    return new Promise( (resolve, reject) => {
        console.log("in readShoutsPromise -"+tex);
        All_Shouts
        .find({})
        .sort([['dateTime', 'ascending']])
        .exec(function (err, data){
            if (err) reject();
            let d = new Date();
            console.log("shouts "+tex+" delete PROMISE = "+data.length +"; date ="+d);
            resolve(data);
        });    
    });
}
0
Ula

Beaucoup de réponses ici mais je n'ai pas vu cette solution simple:

await array.reduce(
  async (promise, member) => await myLongSequentialPromise(member),
  array[0]
)

Preuve: https://jsbin.com/nulafus/1/edit?js,console

0
dimiguel

Sur la base du titre de la question, "Résoudre les promesses les unes après les autres (en séquence)?", Nous pourrions comprendre que le PO s'intéresse davantage au traitement séquentiel des promesses au règlement que des appels séquentiels per se

Cette réponse est offerte:

  • pour démontrer que les appels séquentiels ne sont pas nécessaires pour le traitement séquentiel des réponses.
  • exposer des modèles alternatifs viables aux visiteurs de cette page - y compris le PO s'il est toujours intéressé plus d'un an plus tard.
  • en dépit de l'affirmation du PO qu'il ne souhaite pas effectuer d'appels en même temps, ce peut être véritablement le cas, mais il peut également s'agir d'une hypothèse basée sur le souhait d'un traitement séquentiel des réponses comme le titre l'indique.

Si les appels simultanés ne sont vraiment pas souhaités, consultez la réponse de Benjamin Gruenbaum qui traite de manière exhaustive des appels séquentiels (etc.).

Si toutefois, vous êtes intéressé (pour améliorer les performances) par des modèles autorisant les appels simultanés suivis du traitement séquentiel des réponses, veuillez lire la suite. 

Il est tentant de penser que vous devez utiliser Promise.all(arr.map(fn)).then(fn) (comme je l'ai souvent fait) ou le sucre fantaisie de Promise lib (notamment Bluebird). Cependant (avec le mérite de cet article ) un modèle arr.map(fn).reduce(fn) fera l'affaire, avec le paramètre avantages qu'il:

  • fonctionne avec n'importe quelle bibliothèque de promesse - même les versions pré-conformes de jQuery - seule la fonction .then() est utilisée.
  • offre la souplesse nécessaire pour passer d'une erreur à une autre ou d'une erreur à l'autre, comme vous le souhaitez avec un mod d'une ligne.

La voici écrite pour Q.

var readFiles = function(files) {
    return files.map(readFile) //Make calls in parallel.
    .reduce(function(sequence, filePromise) {
        return sequence.then(function() {
            return filePromise;
        }).then(function(file) {
            //Do stuff with file ... in the correct sequence!
        }, function(error) {
            console.log(error); //optional
            return sequence;//skip-over-error. To stop-on-error, `return error` (jQuery), or `throw  error` (Promises/A+).
        });
    }, Q()).then(function() {
        // all done.
    });
};

Remarque: seul ce fragment, Q(), est spécifique à Q. Pour jQuery, vous devez vous assurer que readFile () renvoie une promesse jQuery. Avec A + libs, les promesses étrangères seront assimilées.

La clé ici est la promesse sequence de la réduction, qui séquence les manipulations des promesses readFile mais pas leur création.

Et une fois que vous avez assimilé cela, c'est peut-être un peu hallucinant de comprendre que l'étape .map() n'est pas réellement nécessaire! L'ensemble de la tâche, les appels parallèles et la gestion en série dans le bon ordre, peut être réalisé avec reduce() seul, avec l'avantage supplémentaire d'une plus grande flexibilité pour:

  • convertissez les appels asynchrones parallèles en appels asynchrones série en déplaçant simplement une ligne, ce qui peut s'avérer utile lors du développement.

Le voici, pour Q encore.

var readFiles = function(files) {
    return files.reduce(function(sequence, f) {
        var filePromise = readFile(f);//Make calls in parallel. To call sequentially, move this line down one.
        return sequence.then(function() {
            return filePromise;
        }).then(function(file) {
            //Do stuff with file ... in the correct sequence!
        }, function(error) {
            console.log(error); //optional
            return sequence;//Skip over any errors. To stop-on-error, `return error` (jQuery), or `throw  error` (Promises/A+).
        });
    }, Q()).then(function() {
        // all done.
    });
};

C'est le modèle de base. Si vous souhaitez également transmettre des données (par exemple, les fichiers ou une transformation de celles-ci) à l'appelant, vous avez besoin d'une variante douce.

0
Roamer-1888

Votre approche n’est pas mauvaise, mais elle a deux problèmes: elle avalise les erreurs et utilise l’antipattern de Construction de la promesse explicite.

Vous pouvez résoudre ces deux problèmes et rendre le code plus propre, tout en appliquant la même stratégie générale:

var Q = require("q");

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

var readFiles = function(files) {
  var readSequential = function(index) {
    if (index < files.length) {
      return readFile(files[index]).then(function() {
        return readSequential(index + 1);
      });
    }
  };

  // using Promise.resolve() here in case files.length is 0
  return Promise.resolve(readSequential(0)); // Start!
};
0
JLRishe