Czy obietnice nie są tylko oddzwanianiem?

Rozwijam JavaScript od kilku lat i w ogóle nie rozumiem zamieszania związanego z obietnicami.

Wygląda na to, że wszystko co robię to zmiana:

api(function(result){
    api2(function(result2){
        api3(function(result3){
             // do work
        });
    });
});

Do czego przydałaby mi się biblioteka typu async w każdym razie, z czymś takim jak:

api().then(function(result){
     api2().then(function(result2){
          api3().then(function(result3){
               // do work
          });
     });
});

Który jest bardziej kodowany i mniej czytelny. Nic tu nie zyskałem, to też nie jest nagle magicznie "płaskie". Nie wspominając już o konieczności przekształcania rzeczy w obietnice.

Więc o co tyle zamieszania obietnice tutaj?

Author: Benjamin Gruenbaum, 2014-03-20

10 answers

Obietnice nie są oddzwanianiem. Obietnica reprezentuje przyszły wynik operacji asynchronicznej . Oczywiście, pisząc je tak, jak to robisz, dostajesz niewielkie korzyści. Ale jeśli piszesz je tak, jak mają być używane, możesz napisać kod asynchroniczny w sposób, który przypomina kod synchroniczny i jest o wiele łatwiejszy do naśladowania: {]}

api().then(function(result){
    return api2();
}).then(function(result2){
    return api3();
}).then(function(result3){
     // do work
});

Oczywiście, nie dużo mniej kodu, ale o wiele bardziej czytelny.

Ale to nie koniec. Odkryjmy prawdziwe korzyści: co jeśli chciałeś sprawdzić, czy nie ma błędu w którymś z kroków? Nie jest to jednak żaden problem, ponieważ nie jest to możliwe.]}
api().then(function(result){
    return api2();
}).then(function(result2){
    return api3();
}).then(function(result3){
     // do work
}).catch(function(error) {
     //handle any error that may occur before this point
});

Prawie tak samo jak try { ... } catch Blok.

Jeszcze lepiej:

api().then(function(result){
    return api2();
}).then(function(result2){
    return api3();
}).then(function(result3){
     // do work
}).catch(function(error) {
     //handle any error that may occur before this point
}).then(function() {
     //do something whether there was an error or not
     //like hiding an spinner if you were performing an AJAX request.
});

I jeszcze lepiej: co jeśli te 3 telefony do api, api2, api3 może działać jednocześnie (np. gdyby były to połączenia AJAX), ale trzeba było poczekać na trzy? Bez obietnic trzeba stworzyć jakiś licznik. Z obietnicami, przy użyciu ES6 notacja, to kolejny kawałek ciasta i całkiem zgrabny:

Promise.all([api(), api2(), api3()]).then(function(result) {
    //do work. result is an array contains the values of the three fulfilled promises.
}).catch(function(error) {
    //handle the error. At least one of the promises rejected.
});

Mam nadzieję, że widzisz teraz obietnice w nowym świetle.

 655
Author: Oscar Paz,
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-02-18 01:56:45

Tak, obietnice są asynchronicznymi odwołaniami. Nie mogą zrobić niczego, czego nie mogą zrobić wywołania zwrotne, a Ty napotykasz te same problemy z asynchronią, co z zwykłymi wywołaniami zwrotnymi.

Jednak obietnice są więcej niż tylko wezwania zwrotne. Są bardzo potężną abstrakcją, pozwalają na czystszy i lepszy, funkcjonalny kod z mniejszą podatnością na błędy.

Więc jaki jest główny pomysł?

Obietnice są obiektami reprezentującymi wynik pojedynczego (asynchronicznego) obliczenia. Oni rozwiąż do tego wyniku tylko raz. Jest kilka rzeczy co to znaczy:

Obiecuje zaimplementować wzór obserwatora:

  • nie musisz znać wywołań zwrotnych, które użyją wartości przed zakończeniem zadania.
  • zamiast oczekiwać wywołań zwrotnych jako argumentów do funkcji, możesz łatwo return Obiekt obietnicy
  • obietnica będzie przechowywać wartość, a Ty możesz transparentnie dodać callback, gdy tylko chcę. Zostanie wywołany, gdy wynik będzie dostępny. "Przejrzystość" oznacza, że gdy masz obietnicę i dodajesz do niej callback, nie ma znaczenia dla Twojego kodu, czy wynik dotarł jeszcze - API i umowy są takie same, co znacznie upraszcza buforowanie/memoizację.
  • możesz łatwo dodać wiele wywołań zwrotnych

Obietnice są łańcuchowe (monadic, jeśli chcesz):

  • Jeśli musisz przekształcić wartość, którą obietnica oznacza, że mapujesz funkcję transformacji nad obietnicą i otrzymujesz nową obietnicę, która reprezentuje przekształcony wynik. Nie można synchronicznie uzyskać wartości, aby ją jakoś wykorzystać, ale można łatwo podnieść transformację w kontekście obietnicy. Brak wywołań zwrotnych.
  • jeśli chcesz połączyć dwa zadania asynchroniczne, możesz użyć metody .then(). Będzie to wymagało wywołania zwrotnego z pierwszym wynikiem i zwraca obietnicę dla wyniku obiecaj, że oddzwanianie wróci.
Brzmi skomplikowanie? Czas na przykład kodu.
var p1 = api1(); // returning a promise
var p3 = p1.then(function(api1Result) {
    var p2 = api2(); // returning a promise
    return p2; // The result of p2 …
}); // … becomes the result of p3

// So it does not make a difference whether you write
api1().then(function(api1Result) {
    return api2().then(console.log)
})
// or the flattened version
api1().then(function(api1Result) {
    return api2();
}).then(console.log)

Spłaszczenie nie przychodzi magicznie, ale można to łatwo zrobić. W przypadku mocno zagnieżdżonego przykładu (w pobliżu) odpowiednikiem będzie

api1().then(api2).then(api3).then(/* do-work-callback */);
Jeśli zobaczenie kodu tych metod pomaga zrozumieć, oto najbardziej podstawowa lib obietnicy w kilku linijkach {20]}.

O co tyle zamieszania z obietnicami?

Abstrakcja obietnicy pozwala znacznie lepiej kompostowalność funkcji. Na przykład, obok then do łączenia łańcuchowego, Funkcja all tworzy obietnicę dla połączonego wyniku wielu równoległych obietnic oczekujących.

Ostatnie, ale nie mniej ważne obietnice są dostarczane ze zintegrowaną obsługą błędów. Wynik obliczeń może być taki, że albo obietnica jest spełniona z wartością, albo jest odrzucona z powodu. Wszystkie funkcje kompozycji obsługują to automatycznie i propagują błędy w łańcuchach obietnic, dzięki czemu nie trzeba dbać o to wyraźnie wszędzie-w przeciwieństwie do zwykłej implementacji callback. Na koniec możesz dodać dedykowany błąd wywołania zwrotnego dla wszystkich wystąpień WYJĄTKÓW.

Nie wspominając o konieczności nawracania rzeczy na obietnice.

To dość trywialne w przypadku dobrych bibliotek promise, zobacz Jak przekonwertować istniejący interfejs API wywołania zwrotnego na promises?

 179
Author: Bergi,
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
2019-06-09 11:32:00

Oprócz już ustalonych odpowiedzi, za pomocą funkcji strzałek ES6 obietnice zmieniają się z skromnie świecącego małego niebieskiego karła prosto W czerwonego olbrzyma. To właśnie zapadnie się w supernową:

api().then(result => api2()).then(result2 => api3()).then(result3 => console.log(result3))

Jak zauważył oligofren , bez argumentów między wywołaniami api nie potrzebujesz w ogóle funkcji Anonymous wrapper:

api().then(api2).then(api3).then(r3 => console.log(r3))

I na koniec, jeśli chcesz osiągnąć poziom supermasywnej czarnej dziury, można oczekiwać obietnic:

async function callApis() {
    let api1Result = await api();
    let api2Result = await api2(api1Result);
    let api3Result = await api3(api2Result);

    return api3Result;
}
 23
Author: John Weisz,
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-05-24 08:23:11

Oprócz wspaniałych odpowiedzi powyżej można dodać jeszcze 2 punkty:

1. Różnica semantyczna:

Obietnice mogą być już rozwiązane po stworzeniu. Oznacza to, że gwarantują warunki, a nie zdarzenia . Jeśli są już rozwiązane, to przekazana do niej funkcja resolved jest nadal wywoływana.

Odwrotnie, wywołania zwrotne obsługują zdarzenia. Tak więc, jeśli wydarzenie, które Cię interesuje, wydarzyło się przed zarejestrowaniem połączenia zwrotnego, połączenie zwrotne jest nie dzwoniłem.

2. Inwersja sterowania

wywołania zwrotne obejmują inwersję sterowania. Po zarejestrowaniu funkcji zwrotnej w dowolnym API, środowisko uruchomieniowe Javascript przechowuje funkcję zwrotną i wywołuje ją z pętli zdarzenia, gdy jest gotowa do uruchomienia.

Zobacz pętlę zdarzeń Javascript , aby uzyskać wyjaśnienie.

Z obietnicami , Kontrola znajduje się w programie wywołującym. The .then () metoda może być wywołana w dowolnym momencie jeśli przechowuj obiekt obietnicy.

 16
Author: dww,
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-18 08:51:17

Oprócz innych odpowiedzi składnia ES2015 płynnie łączy się z obietnicami, redukując jeszcze więcej kodu kotła:

// Sequentially:
api1()
  .then(r1 => api2(r1))
  .then(r2 => api3(r2))
  .then(r3 => {
      // Done
  });

// Parallel:
Promise.all([
    api1(),
    api2(),
    api3()
]).then(([r1, r2, r3]) => {
    // Done
});
 13
Author: Duncan Luk,
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-10-11 15:50:00

Nie, wcale nie.

Wywołania zwrotne są po prostu funkcjami w JavaScript, które mają być wywołane, a następnie wykonane po zakończeniu wykonywania innej funkcji. Więc jak to się dzieje?

W rzeczywistości, w JavaScript, funkcje są same w sobie uważane za obiekty i stąd, jak wszystkie inne obiekty, nawet funkcje mogą być wysyłane jako argumenty do innych funkcji. Najczęstszym i ogólnym przypadkiem użycia jest funkcja setTimeout () w JavaScript.

Promises to nic innego jak znacznie bardziej improwizowane podejście do obsługi i strukturyzacji kodu asynchronicznego w porównaniu do robienia tego samego z wywołaniami zwrotnymi.

Obietnica otrzymuje dwa wywołania zwrotne w funkcji konstruktora: resolve i reject. Te wywołania zwrotne w obietnicach zapewniają nam drobnoziarnistą kontrolę nad obsługą błędów i przypadkami sukcesu. Resolve callback jest używany, gdy wykonanie obietnicy wykonywane pomyślnie i odrzucić callback jest służy do obsługi przypadków błędów.

 6
Author: Ayush Jain,
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
2019-03-06 07:40:04

Obietnice nie są wywołaniami zwrotnymi, oba są idiomami programistycznymi ułatwiającymi programowanie asynchroniczne. Użycie asynchronicznego / oczekującego stylu programowania przy użyciu coroutinów lub generatorów, które zwracają obietnice, można uznać za trzeci taki idiom. Porównanie tych idiomów w różnych językach programowania (w tym Javascript) znajduje się tutaj: https://github.com/KjellSchubert/promise-future-task

 5
Author: Kjell Schubert,
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
2014-04-01 14:09:29

No promises are just wrapper on callback

Przykład Możesz użyć natywnych obietnic javascript z node js

my cloud 9 code link : https://ide.c9.io/adx2803/native-promises-in-node

/**
* Created by dixit-lab on 20/6/16.
*/

var express = require('express');
var request = require('request');   //Simplified HTTP request client.


var app = express();

function promisify(url) {
    return new Promise(function (resolve, reject) {
    request.get(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
        resolve(body);
    }
    else {
        reject(error);
    }
    })
    });
}

//get all the albums of a user who have posted post 100
app.get('/listAlbums', function (req, res) {
//get the post with post id 100
promisify('http://jsonplaceholder.typicode.com/posts/100').then(function (result) {
var obj = JSON.parse(result);
return promisify('http://jsonplaceholder.typicode.com/users/' + obj.userId + '/albums')
})
.catch(function (e) {
    console.log(e);
})
.then(function (result) {
    res.end(result);
}
)

})


var server = app.listen(8081, function () {

var host = server.address().address
var port = server.address().port

console.log("Example app listening at http://%s:%s", host, port)

})


//run webservice on browser : http://localhost:8081/listAlbums
 3
Author: Apoorv,
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-06-20 13:42:01

Obietnice JavaScript faktycznie używają funkcji zwrotnych do określenia, co zrobić po obietnicy została rozwiązana lub odrzucona, dlatego oba nie są zasadniczo różne. Główną ideą obietnic jest odbieranie wywołań zwrotnych-zwłaszcza zagnieżdżonych wywołań zwrotnych, w których chcesz wykonać rodzaj działań, ale byłoby to bardziej czytelne.

 1
Author: Hamid Shoja,
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
2019-12-20 07:49:05

Zapowiedzi:

W JS możemy zawinąć operacje asynchroniczne (np. wywołania bazy danych, wywołania AJAX) w obietnice. Zazwyczaj chcemy uruchomić jakąś dodatkową logikę na pobranych danych. Obietnice JS mają funkcje obsługi, które przetwarzają wynik operacji asynchronicznych. Funkcje obsługi mogą mieć w sobie nawet inne operacje asynchroniczne, które mogą zależeć od wartości poprzednich operacji asynchronicznych.

Obietnica zawsze ma 3 następujące Stany:

  1. oczekujące: stan początkowy każdej obietnicy, ani spełnionej, ani odrzuconej.
  2. spełnione: operacja zakończona pomyślnie.
  3. odrzucono: operacja nie powiodła się.

Oczekująca obietnica może zostać rozwiązana / wypełniona lub odrzucona z wartością. Następnie wywoływane są następujące metody obsługi, które przyjmują wywołania zwrotne jako argumenty:

  1. Promise.prototype.then(): gdy obietnica zostanie rozwiązana, argument wywołania zwrotnego tej funkcji będzie dzwoniłem.
  2. Promise.prototype.catch(): gdy obietnica zostanie odrzucona, zostanie wywołany argument wywołania zwrotnego tej funkcji.

Chociaż powyższe metody dostają argumenty wywołania zwrotnego są znacznie lepsze niż użycie tylko wywołania zwrotne tutaj jest przykład, który wyjaśni wiele:

Przykład

function createProm(resolveVal, rejectVal) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (Math.random() > 0.5) {
                console.log("Resolved");
                resolve(resolveVal);
            } else {
                console.log("Rejected");
                reject(rejectVal);
            }
        }, 1000);
    });
}

createProm(1, 2)
    .then((resVal) => {
        console.log(resVal);
        return resVal + 1;
    })
    .then((resVal) => {
        console.log(resVal);
        return resVal + 2;
    })
    .catch((rejectVal) => {
        console.log(rejectVal);
        return rejectVal + 1;
    })
    .then((resVal) => {
        console.log(resVal);
    })
    .finally(() => {
        console.log("Promise done");
    });
  • funkcja createProm tworzy obietnicę, która jest rozwiązywana lub odrzucana na podstawie losowego Nr po 1 sekundzie
  • jeśli obietnica jest resolved pierwsza metoda then jest wywoływana i wartość resolved jest przekazywana jako argument wywołania zwrotnego
  • jeśli obietnica zostanie odrzucona, pierwsza metoda catch zostanie wywołana i odrzucona wartość zostanie przekazana jako argument
  • metody catch i then zwracają obietnice, dlatego możemy je łańcuchować. Wszystkie zwracane wartości są zawijane w Promise.resolve, a wartości rzucane (używając słowa kluczowego throw) w Promise.reject. Więc każda zwrócona wartość jest przekształcana w obietnicę i na tej obietnicy możemy ponownie wywołaj funkcję obsługi.
  • Łańcuchy obietnic dają nam bardziej precyzyjną kontrolę i lepszy przegląd niż zagnieżdżone wywołania zwrotne. Na przykład metoda catch obsługuje wszystkie błędy, które wystąpiły przed procedurą obsługi catch.
 0
Author: Willem van der Veen,
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
2020-05-06 09:03:41