Co oznacza funkcja then () w JavaScript?

275

Widziałem kod, który wygląda następująco:

myObj.doSome("task").then(function(env) {
    // logic
});

Skąd then()pochodzi?

Kay Pale
źródło
8
ZAKTUALIZOWANO: Dowiedziałem się, że ma to związek z obietnicami API CommonJS sitepen.com/blog/2010/01/19/…
Kay Pale

Odpowiedzi:

348

Tradycyjnym sposobem radzenia sobie z wywołaniami asynchronicznymi w JavaScript są wywołania zwrotne. Powiedzmy, że musieliśmy wykonać trzy połączenia z serwerem, jeden po drugim, aby skonfigurować naszą aplikację. W przypadku wywołań zwrotnych kod może wyglądać mniej więcej tak (przy założeniu funkcji xhrGET do wywołania serwera):

// Fetch some server configuration
    xhrGET('/api/server-config', function(config) {
        // Fetch the user information, if he's logged in
        xhrGET('/api/' + config.USER_END_POINT, function(user) {
            // Fetch the items for the user
            xhrGET('/api/' + user.id + '/items', function(items) {
                // Actually display the items here
            });
        });
    });

W tym przykładzie najpierw pobieramy konfigurację serwera. Następnie na tej podstawie pobieramy informacje o bieżącym użytkowniku, a następnie w końcu otrzymujemy listę elementów dla bieżącego użytkownika. Każde wywołanie xhrGET przyjmuje funkcję zwrotną, która jest wykonywana, gdy serwer odpowiada.

Oczywiście, im więcej poziomów zagnieżdżania mamy, tym trudniej jest czytać, debugować, utrzymywać, aktualizować i zasadniczo pracować z kodem. Jest to ogólnie znane jako piekło zwrotne. Ponadto, jeśli potrzebowaliśmy obsługiwać błędy, musimy przekazać inną funkcję do każdego wywołania xhrGET, aby powiedzieć mu, co należy zrobić w przypadku błędu. Jeśli chcielibyśmy mieć tylko jedną wspólną procedurę obsługi błędów, nie jest to możliwe.

Interfejs API Promise został zaprojektowany w celu rozwiązania tego problemu zagnieżdżania oraz problemu obsługi błędów.

Promise API proponuje następujące opcje:

  1. Każde zadanie asynchroniczne zwróci promiseobiekt.
  2. Każdy promiseobiekt będzie miał thenfunkcję, która może przyjąć dwa argumenty, success moduł obsługi i errormoduł obsługi.
  3. Powodzenie lub procedura obsługi błędów w thenfunkcji zostanie wywołana tylko raz , po zakończeniu zadania asynchronicznego.
  4. thenFunkcja będzie również zwróci promise, w celu umożliwienia łączenia wielu połączeń.
  5. Każdy moduł obsługi (sukces lub błąd) może zwrócić a value, który zostanie przekazany do następnej funkcji jako argument, w łańcuchu promises.
  6. Jeśli moduł obsługi zwraca promise(wykonuje kolejne żądanie asynchroniczne), następny moduł obsługi (sukces lub błąd) zostanie wywołany dopiero po zakończeniu tego żądania.

Tak więc poprzedni przykładowy kod może zostać przetłumaczony na coś takiego, jak przy użyciu obietnic i $httpusługi (w AngularJs):

$http.get('/api/server-config').then(
    function(configResponse) {
        return $http.get('/api/' + configResponse.data.USER_END_POINT);
    }
).then(
    function(userResponse) {
        return $http.get('/api/' + userResponse.data.id + '/items');
    }
).then(
    function(itemResponse) {
        // Display items here
    }, 
    function(error) {
        // Common error handling
    }
);

Propagowanie sukcesu i błędu

Łańcuchowe obietnice to bardzo potężna technika, która pozwala nam osiągnąć wiele funkcji, takich jak wykonanie połączenia z serwerem, wykonanie częściowego przetwarzania danych, a następnie zwrócenie przetworzonych danych do kontrolera. Ale kiedy pracujemy z promisełańcuchami, musimy pamiętać o kilku rzeczach.

Rozważ następujący hipotetyczny promisełańcuch z trzema obietnicami, P1, P2 i P3. Każdy promisema moduł obsługi sukcesu i moduł obsługi błędów, więc S1 i E1 dla P1, S2 i E2 dla P2 oraz S3 i E3 dla P3:

xhrCall()
  .then(S1, E1) //P1
  .then(S2, E2) //P2
  .then(S3, E3) //P3

W normalnym przepływie rzeczy, gdzie nie ma błędów, aplikacja przepływałaby przez S1, S2, a na końcu S3. Ale w prawdziwym życiu rzeczy nigdy nie są tak płynne. P1 może napotkać błąd lub P2 może napotkać błąd, wyzwalając E1 lub E2.

Rozważ następujące przypadki:

• Otrzymujemy pozytywną odpowiedź z serwera w P1, ale zwrócone dane są niepoprawne lub brak danych na serwerze (pomyśl pusta tablica). W takim przypadku, dla następnej obietnicy P2, powinna ona uruchomić moduł obsługi błędów E2.

• Otrzymujemy błąd dla obietnicy P2, wyzwalający E2. Ale w module obsługi mamy dane z pamięci podręcznej, co zapewnia, że ​​aplikacja może załadować się normalnie. W takim przypadku możemy chcieć upewnić się, że po E2 wywoływana jest S3.

Tak więc za każdym razem, gdy piszemy komunikat o sukcesie lub procedurze obsługi błędów, musimy wykonać połączenie - biorąc pod uwagę naszą obecną funkcję, czy ta obietnica jest sukcesem czy porażką następnego programu obsługi w łańcuchu obietnic?

Jeśli chcemy uruchomić procedurę obsługi sukcesu dla następnej obietnicy w łańcuchu, możemy po prostu zwrócić wartość z procedury sukcesu lub procedury obsługi błędów

Jeśli natomiast chcemy uruchomić moduł obsługi błędów dla następnej obietnicy w łańcuchu, możemy to zrobić za pomocą deferredobiektu i wywołania jego reject()metody

Co to jest odroczony obiekt?

Odroczone obiekty w jQuery reprezentują jednostkę pracy, która zostanie ukończona później, zazwyczaj asynchronicznie. Po zakończeniu jednostki pracy deferredobiekt można ustawić jako rozwiązany lub nie powiódł się.

deferredObiektu zawiera promiseobiekt. Za pomocą promiseobiektu możesz określić, co ma się stać po zakończeniu jednostki pracy. Robisz to, ustawiając funkcje wywołania zwrotnego na promiseobiekcie.

Odroczone obiekty w Jquery: https://api.jquery.com/jquery.deferred/

Odroczone obiekty w AngularJs: https://docs.angularjs.org/api/ng/service/ $ q

Sid
źródło
3
Bardzo dobrze napisane. To pomogło mi naprawdę spełnić obietnice.
Ju66ernaut
Czy moduł obsługi błędów, drugi parametr, jest zawsze opcjonalny?
1,21 gigawatów
To zdecydowanie najlepsza odpowiedź, jaką do tej pory widziałem!
Imam Bux
78

funkcja then () jest związana z „obietnicami Javascript” używanymi w niektórych bibliotekach lub frameworkach, takich jak jQuery lub AngularJS.

Obietnica jest wzorcem do obsługi operacji asynchronicznych. Obietnica umożliwia wywołanie metody o nazwie „następnie”, która umożliwia określenie funkcji, które mają być używane jako wywołania zwrotne.

Aby uzyskać więcej informacji, zobacz: http://wildermuth.com/2013/8/3/JavaScript_Promises

A dla obietnic Angular: http://liamkaufman.com/blog/2013/09/09/using-angularjs-promises/

almoraleslopez
źródło
4
więc to jest jak wywołanie zwrotne, które jest wykonywane, gdy zadanie jest wykonane? Jak to inaczej
Muhammad Umer
3
JavaScript Promises w inny komentarz mówi: A promise can only succeed or fail onceiIf a promise has succeeded or failed and you later add a success/failure callback, the correct callback will be called
Xiao
Ponadto bryłki Obietnicy wyjaśniają, jak korzystać promisei co można zrobićcallback
Xiao
Na pierwszej stronie brakuje fragmentów kodu (duże białe spacje). Większość ludzi pomyśli o sprawdzeniu elementu i odnajdzie adresy URL skrzypek poniżej. Ta wiadomość jest dla reszty - skrzypce wciąż działają;)
DanteTheSmith
1
@MuhammadUmer: przeczytaj ten stackoverflow.com/a/31453579/1350476 (odpowiedź Sid)
SharpCoder
32

Według mojej wiedzy, nie ma wbudowanej then()metody w javascript(w momencie pisania tego tekstu).

Wygląda na to, że cokolwiek doSome("task")zwraca, ma metodę o nazwie then.

Jeśli zarejestrujesz wynik powrotu doSome()do konsoli, powinieneś być w stanie zobaczyć właściwości tego, co zostało zwrócone.

console.log( myObj.doSome("task") ); // Expand the returned object in the
                                     //   console to see its properties.

AKTUALIZACJA (od ECMAScript6) : -

.then()Funkcja została włączona do czystego javascript.

Z dokumentacji Mozilli tutaj ,

Metoda then () zwraca obietnicę. Przyjmuje dwa argumenty: funkcje zwrotne dla przypadków sukcesu i niepowodzenia Obietnicy.

Z kolei obiekt Promise jest zdefiniowany jako

Obiekt Promise służy do obliczeń odroczonych i asynchronicznych. Obietnica oznacza operację, która nie została jeszcze zakończona, ale jest oczekiwana w przyszłości.

Oznacza to, że Promisedziała jako symbol zastępczy dla wartości, która nie została jeszcze obliczona, ale zostanie rozwiązana w przyszłości. I .then()funkcja służy do kojarzenia funkcji, które mają zostać wywołane w Obietnicy po jej rozwiązaniu - jako sukces lub porażka.

użytkownik113716
źródło
12
.thenWtedy nie było wbudowanego , ale natywne obietnice nadchodzą teraz w ES6: html5rocks.com/en/tutorials/es6/promises
janfoeh
dzięki za tę odpowiedź, spodziewałem się oddzwonienia od obietnicy, ale okazało się, że była to funkcja zwana „wtedy”, która została zwrócona.
spartikus
15

Oto rzecz, którą sobie stworzyłem, aby wyjaśnić, jak działają rzeczy. Sądzę, że inni też mogą uznać ten konkretny przykład za użyteczny:

doit().then(function() { log('Now finally done!') });
log('---- But notice where this ends up!');

// For pedagogical reasons I originally wrote the following doit()-function so that 
// it was clear that it is a promise. That way wasn't really a normal way to do 
// it though, and therefore Slikts edited my answer. I therefore now want to remind 
// you here that the return value of the following function is a promise, because 
// it is an async function (every async function returns a promise). 
async function doit() {
  log('Calling someTimeConsumingThing');
  await someTimeConsumingThing();
  log('Ready with someTimeConsumingThing');
}

function someTimeConsumingThing() {
  return new Promise(function(resolve,reject) {
    setTimeout(resolve, 2000);
  })
}

function log(txt) {
  document.getElementById('msg').innerHTML += txt + '<br>'
}
<div id='msg'></div>

Magnus
źródło
5

Oto mały JS_Fiddle.

to jest stos wywołania zwrotnego metody, który jest dostępny po rozwiązaniu obietnicy, jest częścią biblioteki takiej jak jQuery, ale teraz jest dostępny w natywnym JavaScript, a poniżej znajduje się szczegółowe wyjaśnienie, jak to działa

Możesz zrobić obietnicę w natywnym JavaScript: tak jak w jQuery są obietnice, każda obietnica może być układana w stos, a następnie może być wywoływana z wywołaniami zwrotnymi Resolve i Reject, w ten sposób możesz łączyć asynchroniczne wywołania.

Rozwidlałem i edytowałem z MSDN Docs na temat stanu ładowania baterii.

Spróbuje dowiedzieć się, czy użytkownik laptopa lub urządzenia ładuje baterię. jest wywoływany i możesz odnieść sukces w pracy.

navigator
    .getBattery()
    .then(function(battery) {
       var charging = battery.charging;
       alert(charging);
    })
    .then(function(){alert("YeoMan : SINGH is King !!");});

Kolejny przykład es6

function fetchAsync (url, timeout, onData, onError) {
    
}
let fetchPromised = (url, timeout) => {
    return new Promise((resolve, reject) => {
        fetchAsync(url, timeout, resolve, reject)
    })
}
Promise.all([
    fetchPromised("http://backend/foo.txt", 500),
    fetchPromised("http://backend/bar.txt", 500),
    fetchPromised("http://backend/baz.txt", 500)
]).then((data) => {
    let [ foo, bar, baz ] = data
    console.log(`success: foo=${foo} bar=${bar} baz=${baz}`)
}, (err) => {
    console.log(`error: ${err}`)
})

Definicja :: then jest metodą stosowaną do rozwiązywania asynchronicznych wywołań zwrotnych

zostało to wprowadzone w ES6

Odpowiednią dokumentację znajdziesz tutaj Obietnice Es6

Tarandeep Singh
źródło
Twoja odpowiedź w rzeczywistości nie odpowiada na pytanie. Podaje tylko przykład użycia interfejsu API bez wyjaśnienia, skąd thenpochodzi i jak działa. Powinieneś poprawić swoją odpowiedź, aby podać te szczegóły.
Didier L,
@TarandeepSingh - w pierwszej instrukcji, w której ostrzegasz o stanie baterii, nie jest zwracany żaden obiekt obiecujący. Więc jaki jest pożytek z drugiego
Mohit Jain
@MohitJain Zaprezentuj, że możesz wykonać wiele połączeń zwrotnych, nawet jeśli nie masz żadnych nowych obietnic. Ponieważ wiele połączeń można również wykonać za pomocą Promise.all.
Tarandeep Singh
CZY masz na myśli „ stos wywołań zwrotnych metod ”?
Bergi,
4

Podejrzewam, że doSome zwraca to, co jest myObj, która również ma metodę then. Standardowe łączenie metod ...

jeśli doSome nie zwraca tego, będąc obiektem, na którym wykonano doSome, bądź pewien, że zwraca jakiś obiekt za pomocą metody then ...

jak wskazuje @patrick, nie ma metody then () dla standardowego js

hvgotcodes
źródło
1
Podejrzewam, że doSome zwraca to - nic nie wymusza / nie uzasadnia takich podejrzeń
Salathiel Genèse
1

doSome („zadanie”) musi zwracać obiekt obietnicy, a obietnica ta zawsze ma funkcję wtedy. Twój kod jest taki jak ten

promise.then(function(env) {
    // logic
}); 

i wiesz, że jest to zwykłe wywołanie funkcji członka.

Hardeep SINGH
źródło
1

.then zwraca obietnicę w funkcji asynchronicznej.

Dobrym przykładem byłoby:

var doSome = new Promise(function(resolve, reject){
    resolve('I am doing something');
});

doSome.then(function(value){
    console.log(value);
});

Aby dodać do niego kolejną logikę, możesz także dodać reject('I am the rejected param')wywołanie funkcji i console.log.

projekty aplikacji
źródło
0

W tym przypadku then()jest metoda klasowa obiektu zwrócona przez doSome()metodę.


źródło
0

Funkcja „.then ()” jest szeroko stosowana dla obiecanych obiektów w programowaniu Asynchoronus dla aplikacji Windows 8 Store. O ile rozumiem, działa to jak połączenie zwrotne.

Znajdź szczegóły w tej dokumentacji http://msdn.microsoft.com/en-us/library/windows/apps/hh700330.aspx

Przyczyną może być również nazwa każdej innej zdefiniowanej funkcji.

użytkownik2508620
źródło
-1

Inny przykład:

new Promise(function(ok) {
   ok( 
      /* myFunc1(param1, param2, ..) */
   )
}).then(function(){
     /* myFunc1 succeed */
     /* Launch something else */
     /* console.log(whateverparam1) */
     /* myFunc2(whateverparam1, otherparam, ..) */
}).then(function(){
     /* myFunc2 succeed */
     /* Launch something else */
     /* myFunc3(whatever38, ..) */
})

Ta sama logika przy użyciu skrótów funkcji strzałek:

new Promise((ok) =>
   ok( 
      /* myFunc1(param1, param2, ..) */
)).then(() =>
     /* myFunc1 succeed */
     /* Launch something else */
     /* Only ONE call or statment can be made inside arrow functions */
     /* For example, using console.log here will break everything */
     /* myFunc2(whateverparam1, otherparam, ..) */
).then(() =>
     /* myFunc2 succeed */
     /* Launch something else */
     /* Only ONE call or statment can be made inside arrow functions */
     /* For example, using console.log here will break everything */
     /* myFunc3(whatever38, ..) */
)

NVRM
źródło
-4

Spóźniłem się około 8 lat, cóż ... tak czy inaczej, tak naprawdę nie wiem, co wtedy () robi, ale może MDN może mieć odpowiedź. Właściwie mógłbym to trochę zrozumieć.

Spowoduje to wyświetlenie wszystkich potrzebnych informacji (mam nadzieję). Chyba że ktoś już opublikował ten link. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then

Format to promise.prototype.then () Obietnica i prototyp są jakby podobnymi zmiennymi, ale nie podobnymi zmiennymi w javascript, mam na myśli, że inne rzeczy idą tam jak navigator.getBattery (). Then (), gdzie to istnieje, ale jest ledwo używany w sieci, ten pokazuje status baterii urządzenia, więcej informacji i więcej na MDN, jeśli jesteś ciekawy.

Jonathan J. Pecany
źródło