Jak prawidłowo zwrócić wiele wartości z obietnicy?

[6]}ostatnio kilka razy natrafiłem na pewną sytuację, której nie wiedziałem, jak właściwie rozwiązać. Przyjmij następujący kod:

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

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

Teraz może pojawić się sytuacja, w której chciałbym mieć dostęp do amazingData w afterSomethingElse.

Oczywistym rozwiązaniem byłoby zwrócenie tablicy lub hasha z afterSomething, ponieważ, cóż, możesz zwrócić tylko jedną wartość z funkcji. Ale zastanawiam się, czy jest sposób, aby afterSomethingElse zaakceptować 2 parametry i wywołać je podobnie, ponieważ wydaje się to dużo łatwiejsze do udokumentowania i zrozumienia.

Zastanawiam się tylko nad tą możliwością, ponieważ istnieje Q.spread, która robi coś podobnego do tego, czego chcę.

Author: Der Hochstapler, 2015-02-24

8 answers

Nie można rozwiązać obietnicy z wieloma właściwościami, tak jak nie można zwrócić wielu wartości z funkcji . Obietnica koncepcyjnie reprezentuje wartość w czasie, więc chociaż można reprezentować wartości złożone, nie można umieścić wielu wartości w obietnicy.

Obietnica z natury rozwiązuje się z pojedynczą wartością - jest to część tego, jak działa Q, Jak działa obietnica / a + spec i jak działa abstrakcja .

Najbliższe, jakie możesz uzyskać, to użycie Q.spread i zwróć tablice lub użyj destrukcji ES6, jeśli jest obsługiwana lub chcesz użyć narzędzia do transpilacji, takiego jak BabelJS.

Jeśli chodzi o przekazywanie kontekstu w dół łańcucha obietnic, proszę odnieść się dodoskonałego kanonika Bergiego na tym .

 54
Author: Benjamin Gruenbaum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:22

Możesz zwrócić obiekt zawierający obie wartości - nie ma w tym nic złego.

Inną strategią jest zachowanie wartości, poprzez zamknięcie, zamiast przechodzenia przez:

somethingAsync().then(afterSomething);

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

W pełni, a nie częściowo w formie (równoważnej, prawdopodobnie bardziej spójnej):

somethingAsync().then(function (amazingData) {
  return processAsync(amazingData).then(function (processedData) {
    // both amazingData and processedData are in scope here
  });
}
 18
Author: Kevin Reid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-24 18:40:56

Możesz przekazać tylko jedną wartość, ale może to być tablica z wielokrotnościami wartości wewnątrz, jak na przykład:

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

Po drugiej stronie można użyć wyrażenia destrukcja dla ES2015, aby uzyskać indywidualne wartości.

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

Aby wywołać obie obietnice, łańcuchując je:

step1()
.then(step2)
.then((msg)=>{
  console.log(msg); // print "done"
})
 14
Author: Alejandro Silva,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-04 04:13:51

Dwie rzeczy, które możesz zrobić, Zwróć obiekt

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;
}
Użyj lunety!
var amazingData;
somethingAsync()
  .then( afterSomething )
  .then( afterSomethingElse )

function afterSomething( returnedAmazingData ) {
  amazingData = returnedAmazingData;
  return processAsync( amazingData );
}
function afterSomethingElse( processedData ) {
  //use amazingData here
}
 3
Author: jemiloii,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-01 21:26:37

Oto, jak powinieneś sobie radzić.

Dzielenie łańcucha

Ponieważ obie funkcje będą używać amazingData , sensowne jest posiadanie ich w dedykowanej funkcji. Zwykle robię to za każdym razem, gdy chcę ponownie użyć niektórych danych, więc zawsze jest obecny jako funkcja arg.

Jako że twój przykład uruchamia jakiś kod, zakładam, że jest on zadeklarowany wewnątrz funkcji. Nazwę to toto () . Wtedy będziemy mieli inną funkcję, która będzie działać zarówno afterSomething () and aftersomething () .

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

Zauważysz również, że dodałem return oświadczenie, ponieważ zwykle jest to droga do obietnic - zawsze zwracasz obietnicę, abyśmy mogli nadal łączyć, jeśli jest to wymagane. Tutaj somethingAsync () wytworzy amazingData i będzie dostępna wszędzie wewnątrz nowej funkcji.

Teraz to, jak będzie wyglądała ta nowa funkcja, zależy zazwyczaj od jest processAsync () także asynchroniczne ?

ProcessAsync nie asynchroniczny

Nie ma powodu, aby komplikować rzeczy, jeśli processAsync () nie jest asynchroniczna. Jakiś stary, dobry kod sekwencyjny by to zrobił.

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

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

Zauważ, że nie ma znaczenia, czy po somethingelse () robi coś asynchronicznego, czy nie. Jeśli tak się stanie, obietnica zostanie zwrócona i łańcuch może być kontynuowany. Jeżeli tak nie jest, wtedy zostanie zwrócona wartość wyniku. Ale ponieważ funkcja jest wywoływana z then () , wartość i tak będzie zawinięta w obietnicę (przynajmniej w surowym Javascript).

ProcessAsync asynchroniczny

Jeśli processAsync () jest asynchroniczne, kod będzie wyglądał nieco inaczej. Tutaj rozważamy afterSomething () i aftersomething () nie będą ponownie używane nigdzie indziej.

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

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

To samo co poprzednio dla po somethingelse () . Może być asynchroniczny lub nie. Obietnica zostanie zwrócona, lub wartość / align = "left" /


Twój styl kodowania jest bardzo zbliżony do tego, co robię, dlatego odpowiadałem nawet po 2 latach. Nie jestem wielkim fanem posiadania anonimowych funkcji wszędzie. Trudno mi to odczytać. Nawet jeśli jest to dość powszechne w społeczności. Jest tak, jak zastąpiliśmy callback-hell {[12] } przez promise-czyściec.

Lubię też trzymać nazwę funkcji w , a następnie krótko. Będą one definiowane tylko lokalnie w każdym razie. I przez większość czasu będą wywoływać inną funkcję zdefiniowaną gdzie indziej - tak wielokrotnego użytku-aby wykonać zadanie. Robię to nawet dla funkcji z tylko 1 parametrem, więc nie muszę pobierać funkcji, gdy dodaję / usuwam parametr do podpisu funkcji.

Przykład jedzenia

Oto przykład:

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

Nie skupiaj się zbytnio na obietnicy .resolve () . To tylko szybki sposób na stworzenie rozwiązanej obietnicy. To, co staram się przez to osiągnąć, to mieć cały kod uruchamiam w jednym miejscu-tuż pod thens . Wszystkie pozostałe funkcje o bardziej opisowej nazwie są wielokrotnego użytku.

Wadą tej techniki jest to, że definiuje ona wiele funkcji. Ale obawiam się, że jest to niezbędny ból, aby uniknąć anonimowych funkcji w całym miejscu. A jakie jest ryzyko: przepełnienie stosu? (żart!)

Użycie tablic lub obiektów zdefiniowanych w innych odpowiedziach również by zadziałało. Ten w pewnym sensie jest to odpowiedź zaproponowana przez Kevina Reida .

Możesz również użyć bind () lub Promise.all () . Pamiętaj, że nadal będą wymagać od Ciebie dzielenia kodu.

Używanie bind

Jeśli chcesz zachować funkcje wielokrotnego użytku, ale tak naprawdę nie musisz trzymać tego, co znajduje się w , to bardzo krótko, możesz użyć bind () .

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

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

Aby było to proste, bind () będzie poprzedzać listę args (z wyjątkiem pierwszego) do funkcja, gdy jest wywoływana.

Używanie Obietnicy.wszystkie

W swoim poście wspomniałeś o użyciu spread () . Nigdy nie używałem frameworka, którego używasz, ale oto, jak powinieneś być w stanie go używać.

Niektórzy wierzą obiecują.all () jest rozwiązaniem wszystkich problemów, więc chyba zasługuje na wzmiankę.

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

Możesz przekazać dane do Promise.all () - zwróć uwagę na obecność tablicy-tak długo, jak obietnice, ale upewnij się, że żadna z obietnic fail w przeciwnym razie zatrzyma przetwarzanie.

I zamiast definiować nowe zmienne z argumentu args , powinieneś być w stanie użyć spread() zamiast then() do wszelkiego rodzaju niesamowitej pracy.

 1
Author: gabriel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:10

Po prostu utwórz obiekt i wyodrębnij argumenty z tego obiektu.

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

Wyciągnij argumenty z promiseResolution.

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);
});
 1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-14 14:49:06

Cokolwiek powrócisz z obietnicy, zostanie owinięte w obietnicę, która zostanie rozpakowana na następnym etapie.

Staje się interesujące, gdy musisz zwrócić jedną lub więcej obietnic obok jednej lub więcej wartości synchronicznych, takich jak;

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

W tych przypadkach konieczne byłoby użycie Promise.all(), aby uzyskać p1 i p2 obietnice rozpakowane na następnym etapie .then(), takie jak

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
Author: Redu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-21 15:49:59

Możesz sprawdzić obserwowalne reprezentowane przez Rxjs , pozwala zwrócić więcej niż jedną wartość .

 0
Author: codelovesme,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-09 19:05:37