web-dev-qa-db-de.com

Wie geben Sie mehrere Werte aus einem Versprechen zurück?

Ich bin kürzlich einige Male in eine bestimmte Situation geraten, die ich nicht richtig lösen konnte. Nehmen Sie den folgenden Code an:

somethingAsync()
  .then( afterSomething )
  .then( afterSomethingElse )

function afterSomething( amazingData ) {
  return processAsync( amazingData );
}
function afterSomethingElse( processedData ) {
}

Nun könnte sich eine Situation ergeben, in der ich auf amazingData in afterSomethingElse zugreifen möchte.

Eine naheliegende Lösung wäre, ein Array oder einen Hash von afterSomething zurückzugeben, da Sie nur einen Wert von einer Funktion zurückgeben können. Aber ich frage mich, ob es einen Weg gibt, afterSomethingElse zwei Parameter zu akzeptieren und es ebenfalls aufzurufen, da dies viel einfacher zu dokumentieren und zu verstehen scheint.

Ich frage mich nur über diese Möglichkeit, da es Q.spread gibt, der etwas tut, was ich will.

55
Der Hochstapler

Sie können ein Versprechen mit mehreren Eigenschaften nicht auflösen, genauso wie Sie nicht mehrere Werte aus einer Funktion zurückgeben können. Ein Versprechen stellt konzeptionell einen Wert über die Zeit dar, sodass Sie zwar zusammengesetzte Werte darstellen können, jedoch nicht mehrere Werte in ein Versprechen setzen können.

Ein Versprechen löst sich inhärent mit einem einzigen Wert auf - dies ist Teil der Funktionsweise von Q, wie die Versprechen/A + -Spezifikation und wie die Abstraktion funktioniert.

Am besten können Sie Q.spread und return-Arrays verwenden oder ES6-Destrukturierung verwenden, wenn dies unterstützt wird oder Sie ein Transpilierungstool wie BabelJS verwenden möchten.

Bezüglich der Weitergabe einer Versprechungskette verweisen wir auf Bergis ausgezeichnete kanonische Eigenschaften dazu .

63

sie können nur einen Wert übergeben, aber es kann ein Array mit mehreren Werten sein. Beispiel:

function step1(){
  let server = "myserver.com";
  let data = "so much data, very impresive";
  return Promise.resolve([server, data]);
}

auf der anderen Seite können Sie den Ausdruck destructuring für ES2015 verwenden, um die einzelnen Werte zu erhalten.

function step2([server, data]){
  console.log(server); // print "myserver.com"
  console.log(data);   // print "so much data, very impresive"
  return Promise.resolve("done");
}

beide Versprechen zu nennen, sie zu verketten:

step1()
.then(step2)
.then((msg)=>{
  console.log(msg); // print "done"
})
18
Alejandro Silva

Sie können ein Objekt mit beiden Werten zurückgeben - daran ist nichts auszusetzen.

Eine andere Strategie besteht darin, den Wert über Closures zu keep, anstatt ihn weiterzuleiten:

somethingAsync().then(afterSomething);

function afterSomething(amazingData) {
  return processAsync(amazingData).then(function (processedData) {
    // both amazingData and processedData are in scope here
  });
}

Vollständige anstatt teilweise inline Form (äquivalent, wohl konsistenter):

somethingAsync().then(function (amazingData) {
  return processAsync(amazingData).then(function (processedData) {
    // both amazingData and processedData are in scope here
  });
}
18
Kevin Reid

Zwei Dinge, die Sie tun können, geben Sie ein Objekt zurück

somethingAsync()
    .then( afterSomething )
    .then( afterSomethingElse );

function processAsync (amazingData) {
     //processSomething
     return {
         amazingData: amazingData, 
         processedData: processedData
     };
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}

function afterSomethingElse( dataObj ) {
    let amazingData = dataObj.amazingData,
        processedData = dataObj.proccessedData;
}

Nutzen Sie den Umfang!

var amazingData;
somethingAsync()
  .then( afterSomething )
  .then( afterSomethingElse )

function afterSomething( returnedAmazingData ) {
  amazingData = returnedAmazingData;
  return processAsync( amazingData );
}
function afterSomethingElse( processedData ) {
  //use amazingData here
}
5
jemiloii

Ich bin der Meinung, dass Sie dies so tun sollten.

die Kette spalten

Da beide Funktionen amazingData verwenden, ist es sinnvoll, sie in einer dedizierten Funktion zu haben. Normalerweise mache ich das jedes Mal, wenn ich einige Daten wiederverwenden möchte, so dass sie immer als eine Funktion arg vorhanden sind.

Da Ihr Beispiel Code ausführt, wird dieser vermutlich in einer Funktion deklariert. Ich werde es toto () nennen. Dann haben wir eine weitere Funktion, die sowohl afterSomething () als auch afterSomethingElse () ausführt.

function toto() {
    return somethingAsync()
        .then( tata );
}

Sie werden auch feststellen, dass ich eine return -Anweisung hinzugefügt habe, da dies normalerweise der Weg zu Versprechen ist. Sie geben immer ein Versprechen zurück, damit wir bei Bedarf weiter verketten können. Hier erzeugt somethingAsync ()amazingData und ist überall in der neuen Funktion verfügbar.

Wie diese neue Funktion normalerweise aussieht, hängt von ist processAsync () auch asynchron ab.

processAsync nicht asynchron

Kein Grund, Dinge zu komplizieren, wenn processAsync () nicht asynchron ist. Ein alter guter sequentieller Code würde es schaffen.

function tata( amazingData ) {
    var processed = afterSomething( amazingData );
    return afterSomethingElse( amazingData, processed );
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}
function afterSomethingElse( amazingData, processedData ) {
}

Beachten Sie, dass es keine Rolle spielt, ob afterSomethingElse () etwas asynchrones tut oder nicht. In diesem Fall wird ein Versprechen zurückgegeben und die Kette kann fortgesetzt werden. Ist dies nicht der Fall, wird der Ergebniswert zurückgegeben. Da die Funktion aber von einem then () aufgerufen wird, wird der Wert trotzdem in ein Versprechen eingeschlossen (zumindest in rohem Javascript).

processAsync asynchron

Wenn processAsync () asynchron ist, sieht der Code etwas anders aus. Hier berücksichtigen wir, dass afterSomething () und afterSomethingElse () nirgendwo anders wiederverwendet werden.

function tata( amazingData ) {
    return afterSomething()
        .then( afterSomethingElse );

    function afterSomething( /* no args */ ) {
        return processAsync( amazingData );
    }
    function afterSomethingElse( processedData ) {
        /* amazingData can be accessed here */
    }
}

Gleich wie zuvor für afterSomethingElse (). Es kann asynchron sein oder nicht. Ein Versprechen wird zurückgegeben oder ein Wert in ein gelöstes Versprechen eingewickelt.


Ihr Codierungsstil kommt dem, was ich bisher gemacht habe, ziemlich nahe. Deshalb habe ich auch nach 2 Jahren geantwortet. Ich bin kein großer Fan von anonymen Funktionen überall. Ich finde es schwer zu lesen. Auch wenn es in der Gemeinde durchaus üblich ist. Es ist, als wir die Rückruf-Hölle durch ein Versprechen-Fegefeuer ersetzten.

Ich halte auch gerne den Namen der Funktionen im then kurz. Sie werden ohnehin nur lokal definiert. Und die meiste Zeit rufen sie eine andere Funktion auf, die an anderer Stelle definiert ist - also wiederverwendbar -, um die Arbeit zu erledigen. Ich mache das sogar für Funktionen mit nur 1 Parameter, so dass ich die Funktion nicht ein- und aussteigen muss, wenn ich der Funktionssignatur einen Parameter hinzufüge/entferne.

Beispiel essen

Hier ist ein Beispiel:

function goingThroughTheEatingProcess(plenty, of, args, to, match, real, life) {
    return iAmAsync()
        .then(chew)
        .then(swallow);

        function chew(result) {
            return carefullyChewThis(plenty, of, args, "water", "piece of tooth", result);
        }

        function swallow(wine) {
            return nowIsTimeToSwallow(match, real, life, wine);
        }
}

function iAmAsync() {
    return Promise.resolve("mooooore");
}

function carefullyChewThis(plenty, of, args, and, some, more) {
    return true;
}

function nowIsTimeToSwallow(match, real, life, bobool) {
}

Konzentrieren Sie sich nicht zu sehr auf Promise.resolve (). Es ist nur eine schnelle Möglichkeit, ein gelöstes Versprechen zu erstellen. Damit versuche ich, den gesamten Code, den ich ausführe, an einem einzigen Ort zu haben - direkt unter dem thens. Alle anderen Funktionen mit einem aussagekräftigeren Namen können wiederverwendet werden.

Der Nachteil dieser Technik ist, dass sie viele Funktionen definiert. Aber es ist ein notwendiger Schmerz, den ich fürchte, um nicht überall anonyme Funktionen zu haben. Und was ist das Risiko überhaupt: ein Stapelüberlauf? (Scherz!)


Die Verwendung von Arrays oder Objekten, wie in anderen Antworten definiert, würde ebenfalls funktionieren. In gewisser Weise ist dies das Antwort von Kevin Reid .

Sie können auch bind () oder Promise.all () verwenden. Beachten Sie, dass Sie weiterhin Ihren Code aufteilen müssen.

mit binden

Wenn Sie Ihre Funktionen wiederverwendbar halten möchten, aber nicht wirklich das, was sich in then befindet, sehr kurz halten müssen, können Sie bind () verwenden.

function tata( amazingData ) {
    return afterSomething( amazingData )
        .then( afterSomethingElse.bind(null, amazingData) );
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}
function afterSomethingElse( amazingData, processedData ) {
}

Um es einfach zu halten: bind () stellt der aufgerufenen Funktion die Liste der Argumente (mit Ausnahme der ersten) voran.

mit Promise.all

In Ihrem Beitrag haben Sie die Verwendung von spread () erwähnt. Ich habe nie das Framework verwendet, das Sie verwenden, aber hier ist, wie Sie es verwenden können sollten.

Einige glauben, dass Promise.all () die Lösung für alle Probleme ist, daher verdient es meiner Meinung nach Erwähnung.

function tata( amazingData ) {
    return Promise.all( [ amazingData, afterSomething( amazingData ) ] )
        .then( afterSomethingElse );
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}
function afterSomethingElse( args ) {
    var amazingData = args[0];
    var processedData = args[1];
}

Sie können Daten an Promise.all () übergeben - beachten Sie das Vorhandensein des Arrays - solange Versprechen vorliegen, stellen Sie jedoch sicher, dass keines der Versprechen fehlschlägt, da sonst die Verarbeitung unterbrochen wird.

Und anstatt neue Variablen aus dem Argument args zu definieren, sollten Sie spread () anstelle von then () für alle Arten von verwenden können Gute Arbeit.

2
gabriel

Machen Sie einfach ein Objekt und extrahieren Sie Argumente aus diesem Objekt.

let checkIfNumbersAddToTen = function (a, b) {
return new Promise(function (resolve, reject) {
 let c = parseInt(a)+parseInt(b);
 let promiseResolution = {
     c:c,
     d : c+c,
     x : 'RandomString'
 };
 if(c===10){
     resolve(promiseResolution);
 }else {
     reject('Not 10');
 }
});
};

Argumente aus promiseResolution ziehen.

checkIfNumbersAddToTen(5,5).then(function (arguments) {
console.log('c:'+arguments.c);
console.log('d:'+arguments.d);
console.log('x:'+arguments.x);
},function (failure) {
console.log(failure);
});
2
user6507599

Was auch immer Sie von einem Versprechen zurückgeben, wird in ein Versprechen eingepackt, das in der nächsten .then()-Phase ausgepackt wird.

Es wird interessant, wenn Sie ein oder mehrere Zusagen zusammen mit einem oder mehreren synchronen Werten wie z.

Promise.resolve([Promise.resolve(1), Promise.resolve(2), 3, 4])
       .then(([p1,p2,n1,n2]) => /* p1 and p2 are still promises */);

In diesen Fällen ist es wichtig, Promise.all() zu verwenden, um p1 und p2-Versprechen in der nächsten .then()-Phase, wie z

Promise.resolve(Promise.all([Promise.resolve(1), Promise.resolve(2), 3, 4]))
       .then(([p1,p2,n1,n2]) => /* p1 is 1, p2 is 2, n1 is 3 and n2 is 4 */);
0
Redu

Sie können Observable durch Rxjs darstellen. Sie können mehr als einen - Wert zurückgeben.

0
codelovesme