web-dev-qa-db-de.com

Versprechen nacheinander lösen (d. H. In Folge)?

Betrachten Sie den folgenden Code, der ein Array von Dateien seriell/sequentiell liest. readFiles gibt ein Versprechen zurück, das erst aufgelöst wird, wenn alle Dateien der Reihe nach gelesen wurden.

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!

  });
};

Der obige Code funktioniert, aber ich mag keine Rekursion, damit die Dinge nacheinander auftreten. Gibt es eine einfachere Möglichkeit, diesen Code umzuschreiben, damit ich meine komische readSequential-Funktion nicht verwenden muss?

Ursprünglich habe ich versucht, Promise.all zu verwenden, aber das führte dazu, dass alle readFile-Aufrufe gleichzeitig ablaufen, was nicht was ich will:

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

Update 2017 : Ich würde eine asynchrone Funktion verwenden, wenn die Umgebung dies unterstützt:

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

Wenn Sie möchten, können Sie das Lesen der Dateien verzögern, bis Sie sie mit einem Asynchrongenerator benötigen (sofern Ihre Umgebung dies unterstützt):

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

Update: Im zweiten Gedanken - ich könnte stattdessen eine for-Schleife verwenden:

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

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

Oder kompakter mit reduzierten:

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

In anderen Versprechenbibliotheken (z. B. wann und Bluebird) gibt es hierfür Methoden.

Bluebird wäre zum Beispiel:

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

Zwar gibt es wirklich keinen Grund, not, async heute zu verwenden.

220

So führe ich es vor, Aufgaben in Serie auszuführen.

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

Was ist mit Fällen mit mehr Aufgaben? Wie 10?

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

Diese Frage ist alt, aber wir leben in einer Welt von ES6 und funktionalem JavaScript. Sehen wir uns also an, wie wir uns verbessern können.

Da Versprechen sofort ausgeführt werden, können wir nicht nur eine Reihe von Versprechen erstellen, sie würden alle gleichzeitig abfeuern.

Stattdessen müssen wir ein Array von Funktionen erstellen, das ein Versprechen zurückgibt. Jede Funktion wird dann nacheinander ausgeführt, wodurch das Versprechen darin beginnt.

Wir können das auf ein paar Arten lösen, aber am liebsten nutze ich reduce.

Mit reduce in Kombination mit Versprechungen wird es etwas kniffelig, daher habe ich den einen Liner in einige kleinere, leicht verdauliche Bisse zerlegt.

Die Essenz dieser Funktion besteht darin, reduce zu verwenden, beginnend mit einem Anfangswert von Promise.resolve([]) oder einem Versprechen, das ein leeres Array enthält.

Dieses Versprechen wird dann als reduce an die promise-Methode übergeben. Dies ist der Schlüssel zur Verkettung jedes Versprechens nacheinander. Das nächste auszuführende Versprechen ist func. Wenn die then ausgelöst wird, werden die Ergebnisse verkettet und dieses Versprechen wird zurückgegeben, wobei der reduce-Zyklus mit der nächsten Versprechungsfunktion ausgeführt wird.

Sobald alle Versprechen ausgeführt wurden, enthält das zurückgegebene Versprechen ein Array aller Ergebnisse jedes Versprechens.

ES6-Beispiel (ein Liner)

/*
 * 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-Beispiel (aufgeschlüsselt)

// 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([]))

Verwendungszweck:

// 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

Dazu einfach in 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

Einfache Verwendung für das Standardversprechen von Node.js:

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

UPDATE

Einzelteile-Versprechen ist ein gebrauchsfertiges NPM-Paket, das dasselbe tut.

23
Pooya

Ich musste viele sequenzielle Aufgaben ausführen und diese Antworten verwenden, um eine Funktion zu erstellen, die sich um die Bearbeitung aller sequenziellen Aufgaben kümmert ...

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

Die Funktion akzeptiert 2 Argumente + 1 optional. Das erste Argument ist das Array, an dem wir arbeiten werden. Das zweite Argument ist die Task selbst, eine Funktion, die ein Versprechen zurückgibt. Die nächste Task wird nur gestartet, wenn dieses Versprechen aufgelöst wird. Das dritte Argument ist ein Rückruf, der ausgeführt wird, wenn alle Aufgaben ausgeführt wurden. Wenn kein Rückruf übergeben wird, gibt die Funktion das erstellte Versprechen zurück, damit wir das Ende abwickeln können.

Hier ist ein Beispiel für die Verwendung:

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

Hoffe es erspart jemandem etwas Zeit ...

11
Salketer

Dies ist eine geringfügige Abweichung von einer anderen Antwort oben. Native Versprechen verwenden:

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

Erklärung

Wenn Sie diese Aufgaben [t1, t2, t3] haben, entspricht das Obige Promise.resolve().then(t1).then(t2).then(t3). Es ist das Verhalten von reduzieren.

Wie benutzt man

First Sie müssen eine Aufgabenliste erstellen! Eine Aufgabe ist eine Funktion, die kein Argument akzeptiert. Wenn Sie Ihrer Funktion Argumente übergeben müssen, verwenden Sie bind oder andere Methoden, um eine Aufgabe zu erstellen. Zum Beispiel:

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

Meine bevorzugte Lösung:

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

Es unterscheidet sich nicht grundlegend von anderen hier veröffentlichten, aber:

  • Wendet die Funktion auf Artikel in Serie
  • Wird in eine Reihe von Ergebnissen aufgelöst
  • Benötigt kein async/waitit (Unterstützung ist noch recht begrenzt, ca. 2017)
  • Verwendet Pfeilfunktionen; Schön und prägnant

Verwendungsbeispiel:

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

Getestet auf vernünftigen aktuellen Chrome (v59) und NodeJS (v8.1.2).

4
Molomby

Die schönste Lösung, die ich herausfinden konnte, war mit bluebird Versprechen. Sie können einfach Promise.resolve(files).each(fs.readFileAsync); tun, was garantiert, dass die Versprechen der Reihe nach in der Reihenfolge aufgelöst werden.

4
Mikael Lepistö

Verwenden Sie Array.prototype.reduce und denken Sie daran, Ihre Versprechen in eine Funktion zu packen, da sie sonst bereits ausgeführt werden!

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

Schön und einfach ... Sie sollten in der Lage sein, denselben Samen für die Leistung usw. erneut zu verwenden.

2
Alexander Mills

Ich habe diese einfache Methode für das Promise-Objekt erstellt:

Erstellen Sie eine Promise.sequence-Methode und fügen Sie sie dem Promise-Objekt hinzu

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

Verwendungszweck:

var todo = [];

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

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

Das Beste an dieser Erweiterung des Promise-Objekts ist, dass es dem Stil der Versprechen entspricht. Promise.all und Promise.sequence werden auf dieselbe Weise aufgerufen, haben jedoch unterschiedliche Semantiken.

Vorsicht

Die sequentielle Abwicklung von Versprechen ist in der Regel keine sehr gute Möglichkeit, Versprechungen zu verwenden. Es ist normalerweise besser, Promise.all zu verwenden, und den Browser den Code so schnell wie möglich ausführen zu lassen. Es gibt jedoch echte Anwendungsfälle - zum Beispiel beim Schreiben einer mobilen App mit Javascript.

2
frodeborli

@ Joelnets Antwort hat mir sehr gefallen, aber für mich ist diese Art der Codierung etwas schwer zu verstehen, also habe ich ein paar Tage damit verbracht, herauszufinden, wie ich dieselbe Lösung lesbarer formulieren würde und dies ist meine Nehmen Sie, nur mit einer anderen Syntax und einigen Kommentaren.

// 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

Sie können diese Funktion verwenden, um die Liste promiseFactories abzurufen:

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

Promise Factory ist nur eine einfache Funktion, die ein Versprechen zurückgibt:

function myPromiseFactory() {
    return somethingThatCreatesAPromise();
}

Es funktioniert, weil eine Versprechenfabrik das Versprechen nicht schafft, bis es dazu aufgefordert wird. Es funktioniert genauso wie eine dann-Funktion - tatsächlich ist es das Gleiche!

Sie möchten nicht mit einer ganzen Reihe von Versprechen arbeiten. Nach der Versprechenspezifikation beginnt die Ausführung, sobald ein Versprechen erstellt wird. Was Sie wirklich wollen, ist eine Reihe von Versprechungsfabriken ...

Wenn Sie mehr über Versprechen erfahren möchten, sollten Sie diesen Link überprüfen: https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html

2
sidanmor

Ich verwende den folgenden Code, um das Promise-Objekt zu erweitern. Es behandelt die Ablehnung der Versprechen und gibt eine Reihe von Ergebnissen zurück 

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

Beispiel

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

Wenn Sie möchten, können Sie die Verwendung von verkleinern, um ein sequentielles Versprechen zu machen.

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

es funktioniert immer in sequentieller Reihenfolge.

1
Victor Palomo

Wie Bergi bemerkt hat, denke ich, ist die beste und klarste Lösung BlueBird.each, Code unten:

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

Meine Antwort basiert auf 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;
    });
  };

Diese Lösung gibt die Ergebnisse als Array wie Promise.all () zurück.

Verwendungszweck:

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

Die Array-Push- und -Pop-Methode kann für die Abfolge von Versprechungen verwendet werden. Sie können auch neue Versprechen pushen, wenn Sie zusätzliche Daten benötigen. Dies ist der Code, den ich in React Infinite Loader verwenden werde, um eine Folge von Seiten zu laden.

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

Ich verstehe nicht, warum die Leute so komplexe Lösungen vorschlagen. Hier ist eine einfachere Logik

Mit Async/Await - Beste Lösung, wenn Sie die Unterstützung von ES7 haben

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

Ohne Async/Warten

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

Wenn eine andere Person einen garantierten Weg zur STRICTLY sequentiellen Lösung des Versprechens von CRUD-Vorgängen benötigt, können Sie auch den folgenden Code als Grundlage verwenden.

Solange Sie vor dem Aufruf jeder Funktion 'return' hinzufügen und ein Promise beschreiben, und dieses Beispiel als Grundlage verwenden, wird der nächste Aufruf der .then () - Funktion NACH dem Abschluss des vorherigen konsistent gestartet:

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

Viele Antworten hier, aber ich habe diese einfache Lösung nicht gesehen:

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

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

0
dimiguel

Auf der Grundlage des Titels der Frage, "Versprechen nacheinander lösen (d. H. In Folge)?", Könnten wir verstehen, dass das OP mehr an der sequentiellen Behandlung von Versprechen bei Abwicklung interessiert ist als sequenzielle Aufrufe per se

Diese Antwort wird angeboten:

  • um zu zeigen, dass sequenzielle Aufrufe für die sequentielle Behandlung von Antworten nicht erforderlich sind.
  • um den Besuchern dieser Seite realisierbare alternative Muster zu zeigen - einschließlich des OP, wenn er über ein Jahr später noch interessiert ist.
  • trotz der Behauptung des OP, dass er keine gleichzeitigen Anrufe tätigen möchte, kann dies tatsächlich der Fall sein, aber ebenso eine Annahme, die auf dem Wunsch nach sequenzieller Behandlung von Antworten basiert, wie der Titel impliziert.

Wenn gleichzeitige Anrufe wirklich nicht erwünscht sind, lesen Sie die Antwort von Benjamin Gruenbaum, die alle sequenziellen Anrufe (usw.) ausführlich behandelt.

Wenn Sie jedoch (für eine verbesserte Leistung) an Mustern interessiert sind, die gleichzeitige Anrufe gefolgt von einer sequentiellen Behandlung von Antworten zulassen, lesen Sie bitte weiter. 

Es ist verlockend zu glauben, dass Sie Promise.all(arr.map(fn)).then(fn) (wie ich es schon oft getan habe) oder einen ausgefallenen Zucker von Promise lib (insbesondere Bluebirds) verwenden müssen, jedoch (mit einem Verdienst von diesem Artikel ) ein arr.map(fn).reduce(fn)-Muster die Aufgabe erledigen wird Vorteile, dass es:

  • funktioniert mit jeder Versprechenbibliothek - auch bei vorkonformen Versionen von jQuery - wird nur .then() verwendet.
  • bietet die Flexibilität zum Überspringen oder Stop-On-Fehler, je nachdem, was Sie mit einem einzeiligen Mod wünschen.

Hier ist es für Q geschrieben.

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

Hinweis: Nur dieses eine Fragment, Q(), ist spezifisch für Q. Für jQuery müssen Sie sicherstellen, dass readFile () ein jQuery-Versprechen zurückgibt. Mit A + libs werden ausländische Versprechungen assimiliert.

Der Schlüssel hier ist das sequence-Versprechen der Reduktion, das die Handhabung der readFile verspricht, aber nicht ihre Erstellung.

Und wenn Sie das einmal in sich aufgenommen haben, ist es vielleicht etwas umwerfend, wenn Sie feststellen, dass die Phase .map() nicht unbedingt notwendig ist! Der gesamte Job, parallele Abrufe und serielle Abwicklung in der richtigen Reihenfolge, kann nur mit reduce() und zusätzlichem Vorteil der weiteren Flexibilität erreicht werden:

  • konvertieren Sie von parallelen asynchronen Aufrufen zu seriellen asynchronen Aufrufen, indem Sie einfach eine Zeile verschieben - möglicherweise während der Entwicklung nützlich.

Hier ist es wieder für Q.

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

Das ist das Grundmuster. Wenn Sie dem Anrufer auch Daten übermitteln möchten (z. B. die Dateien oder eine Umwandlung davon), benötigen Sie eine einfache Variante.

0
Roamer-1888

Ihr Ansatz ist nicht schlecht, hat aber zwei Probleme: Er schluckt Fehler und verwendet das Explicit Promise Construction Antipattern.

Sie können beide Probleme lösen und den Code sauberer machen, wobei Sie immer noch dieselbe allgemeine Strategie verwenden:

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