Jak zwrócić odpowiedź z połączenia asynchronicznego?

Mam funkcję foo, która wysyła żądanie Ajax. Jak Mogę zwrócić odpowiedź foo?

Próbowałem zwracać wartość z wywołania zwrotnego success, a także przypisywać odpowiedź do zmiennej lokalnej wewnątrz funkcji i zwracać tę, ale żaden z tych sposobów nie zwraca odpowiedzi.

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            result = response;
            // return response; // <- I tried that one as well
        }
    });

    return result;
}

var result = foo(); // It always ends up being `undefined`.
Author: Ry-, 2013-01-08

30 answers

→ aby uzyskać bardziej ogólne wyjaśnienie asynchronicznego zachowania z różnymi przykładami, zobacz Dlaczego moja zmienna pozostaje niezmieniona po zmodyfikowaniu jej wewnątrz funkcji? - Asynchroniczny kod odniesienia

→ jeśli już rozumiesz problem, przejdź do możliwych rozwiązań poniżej.

Problem

A W Ajax oznacza asynchroniczne . Oznacza to wysłanie żądanie (a raczej otrzymanie odpowiedzi)jest usuwane z normalnego przepływu realizacji. W twoim przykładzie $.ajax zwraca natychmiast i następne polecenie, return result;, jest wykonywane przed wywołaniem funkcji, którą przekazałeś jako success.

Jest to analogia, która, miejmy nadzieję, czyni różnicę między synchronicznym i asynchronicznym przepływem jaśniejszym:]}

Synchroniczne

Wyobraź sobie, że dzwonisz do przyjaciela i prosisz go, żeby coś dla Ciebie znalazł. Chociaż to może trochę potrwać, czekasz na telefon i gapisz się w kosmos, aż twój przyjaciel da ci odpowiedź, której potrzebujesz.

To samo dzieje się, gdy wykonujesz wywołanie funkcji zawierające "normalny" kod:

function findItem() {
    var item;
    while(item_not_found) {
        // search
    }
    return item;
}

var item = findItem();

// Do something with item
doSomethingElse();

Mimo że wykonanie findItem może zająć dużo czasu, każdy kod pojawiający się po var item = findItem(); musi poczekać aż funkcja zwróci wynik.

Asynchroniczny

Dzwonisz do przyjaciela z tego samego powodu. Ale tym razem powiedz mu, że spieszysz się i powinien oddzwonić na Twój telefon komórkowy. Rozłączasz się, wychodzisz z domu i robisz to, co planujesz. Gdy twój przyjaciel oddzwoni, masz do czynienia z informacjami, które ci dał.

To jest dokładnie to, co się dzieje, gdy robisz żądanie Ajax.

findItem(function(item) {
    // Do something with item
});
doSomethingElse();

Zamiast czekać na odpowiedź, wykonanie jest kontynuowane natychmiast, a instrukcja po wywołaniu Ajax jest wykonywana. Aby w końcu uzyskać odpowiedź, podajesz funkcja, która ma być wywołana po otrzymaniu odpowiedzi, wywołanie zwrotne (zauważ coś? Oddzwonić ?). Każde polecenie pojawiające się po tym wywołaniu jest wykonywane przed wywołaniem wywołania zwrotnego.


Roztwór(y)

Wykorzystaj asynchroniczną naturę JavaScript! podczas gdy niektóre operacje asynchroniczne zapewniają synchroniczne odpowiedniki (podobnie jak "Ajax"), generalnie nie zaleca się ich używania, szczególnie w kontekście przeglądarki.

Why is It bad do pytasz?

JavaScript działa w wątku interfejsu przeglądarki, a każdy długo działający proces zablokuje interfejs, co spowoduje, że przestanie odpowiadać. Dodatkowo istnieje górny limit czasu wykonania dla JavaScript, a przeglądarka zapyta użytkownika, czy kontynuować wykonywanie, czy nie.

To wszystko jest naprawdę złe doświadczenie użytkownika. Użytkownik nie będzie w stanie stwierdzić, czy wszystko działa dobrze, czy nie. Ponadto efekt będzie gorszy dla użytkowników z powolnym połączenie.

W dalszej części przyjrzymy się trzem różnym rozwiązaniom, które budują się jeden na drugim:]}
  • obietnice z async/await (system ES2017+ jest dostępny w starszych przeglądarkach, jeśli używasz transpilera lub regeneratora.]}
  • Callbacks (popularne w node)
  • obietnice z then() (ES2015+, dostępny w starszych przeglądarkach, jeśli używasz jednej z wielu bibliotek obietnic)

Wszystkie trzy to dostępne w aktualnych przeglądarkach i node 7+.


ES2017+: obietnice z async/await

Wersja ECMAScript wydana w 2017 roku wprowadziła obsługę składni na poziomiedla funkcji asynchronicznych. Za pomocą async i await można pisać asynchroniczne w "stylu synchronicznym". Kod jest nadal asynchroniczny, ale jest łatwiejszy do odczytania/zrozumienia.

async/await opiera się na obietnicach: funkcja {[22] } zawsze zwraca obietnicę. await "rozpakowuje" obietnicę i albo powoduje wartość, z którą obietnica została rozwiązana, albo rzuca błąd, jeśli obietnica została odrzucona.

Ważne: możesz używać tylko await wewnątrz funkcji async. Oznacza to, że na najwyższym poziomie nadal musisz pracować bezpośrednio z obietnicą.

Możesz przeczytać więcej o async oraz await na MDN.

Oto przykład, który opiera się na opóźnieniu powyżej:]}
// Using 'superagent' which will return a promise.
var superagent = require('superagent')

// This is isn't declared as `async` because it already returns a promise
function delay() {
  // `delay` returns a promise
  return new Promise(function(resolve, reject) {
    // Only `delay` is able to resolve or reject the promise
    setTimeout(function() {
      resolve(42); // After 3 seconds, resolve the promise with value 42
    }, 3000);
  });
}


async function getAllBooks() {
  try {
    // GET a list of book IDs of the current user
    var bookIDs = await superagent.get('/user/books');
    // wait for 3 seconds (just for the sake of this example)
    await delay();
    // GET information about each book
    return await superagent.get('/books/ids='+JSON.stringify(bookIDs));
  } catch(error) {
    // If any of the awaited promises was rejected, this catch block
    // would catch the rejection reason
    return null;
  }
}

// Async functions always return a promise
getAllBooks()
  .then(function(books) {
    console.log(books);
  });

Current browser i node obsługuje wersje async/await. Można również obsługiwać starsze środowiska, przekształcając kod do ES5 za pomocą regenerator (lub narzędzi używających regenerator, takich jak Babel ).


Niech funkcje akceptują wywołania zwrotne

Wywołanie zwrotne jest po prostu funkcją przekazywaną do innej funkcji. Ta inna funkcja może wywołać funkcję przekazaną, gdy jest gotowa. W kontekście procesu asynchronicznego, wywołanie zwrotne zostanie wywołane za każdym razem, gdy zostanie wykonany proces asynchroniczny. Zazwyczaj wynik jest przekazywany do wywołania zwrotnego.

W przykładzie pytania, można zrobić foo zaakceptować callback i użyć go jako success callback. Więc to

var result = foo();
// Code that depends on 'result'

Staje się

foo(function(result) {
    // Code that depends on 'result'
});

Tutaj zdefiniowaliśmy funkcję "inline", ale możesz przekazać dowolną referencję funkcji:

function myCallback(result) {
    // Code that depends on 'result'
}

foo(myCallback);

foo sama definicja jest następująca:

function foo(callback) {
    $.ajax({
        // ...
        success: callback
    });
}

callback będzie odnosić się do funkcji, którą przekazujemy foo kiedy ją wywołujemy i po prostu przekazujemy ją success. Tzn. gdy żądanie Ajax zakończy się sukcesem, $.ajax wywoła callback i przekaże odpowiedź na wywołanie zwrotne (do którego można się odnieść za pomocą result, ponieważ tak zdefiniowaliśmy wywołanie zwrotne).

Możesz również przetworzyć odpowiedź przed przekazaniem jej do wywołania zwrotnego:]}
function foo(callback) {
    $.ajax({
        // ...
        success: function(response) {
            // For example, filter the response
            callback(filtered_response);
        }
    });
}

Łatwiej jest pisać kod za pomocą wywołań zwrotnych, niż mogłoby się wydawać. W końcu JavaScript w przeglądarce jest silnie sterowany zdarzeniami (zdarzenia DOM). Odbieranie Ajaksu odpowiedź jest niczym innym jak wydarzeniem.
Trudności mogą pojawić się, gdy musisz pracować z kodem innych firm, ale większość problemów można rozwiązać, po prostu myśląc o przepływie aplikacji.


ES2015+: Promises with then ()

Promise API jest nową funkcją ECMAScript 6 (ES2015), ale ma już dobrą obsługę przeglądarki . Istnieje również wiele bibliotek, które implementują standardowe API Promises i zapewniają dodatkowe metody ułatwiające korzystanie i skład funkcji asynchronicznych (np. ).

Obietnice są kontenerami dla przyszłych wartości. Gdy obietnica otrzyma wartość (jest rozwiązana) lub gdy zostanie anulowana (odrzucona ), powiadamia wszystkich swoich "słuchaczy", którzy chcą uzyskać dostęp do tej wartości.

Przewaga nad zwykłymi wywołaniami zwrotnymi polega na tym, że pozwalają one na oddzielenie kodu i są łatwiejsze do skomponowania.

Oto prosty przykład użycia obietnicy:

function delay() {
  // `delay` returns a promise
  return new Promise(function(resolve, reject) {
    // Only `delay` is able to resolve or reject the promise
    setTimeout(function() {
      resolve(42); // After 3 seconds, resolve the promise with value 42
    }, 3000);
  });
}

delay()
  .then(function(v) { // `delay` returns a promise
    console.log(v); // Log the value once it is resolved
  })
  .catch(function(v) {
    // Or do something else if it is rejected 
    // (it would not happen in this example, since `reject` is not called).
  });

Zastosowane do naszego wywołania Ajax możemy użyć obietnic takich jak:

function ajax(url) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
      resolve(this.responseText);
    };
    xhr.onerror = reject;
    xhr.open('GET', url);
    xhr.send();
  });
}

ajax("/echo/json")
  .then(function(result) {
    // Code depending on result
  })
  .catch(function() {
    // An error occurred
  });

Opisanie wszystkich zalet oferty promise wykracza poza zakres tej odpowiedzi, ale jeśli piszesz nowy kod, powinieneś poważnie je rozważyć. Zapewniają one wielką abstrakcję i separację kodu.

Więcej informacji o obietnicach: HTML5 rocks-obietnice JavaScript

Side note: jQuery ' s deferred obiekty

Deferred objects są niestandardową implementacją obietnic jQuery(przed standaryzacją API Promise). Zachowują się prawie jak obietnice, ale ujawniają nieco inne API.

JQuery może być używane w wielu językach, takich jak np. Angielski, Angielski, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki, Niemiecki]}
function ajax() {
    return $.ajax(...);
}

ajax().done(function(result) {
    // Code depending on result
}).fail(function() {
    // An error occurred
});

Uwaga: obietnica gotchas

Należy pamiętać, że obietnice i Obiekty odroczone są tylko kontenery dla przyszłej wartości, nie są samą wartością. Na przykład, załóżmy, że masz następujące:

function checkPassword() {
    return $.ajax({
        url: '/password',
        data: {
            username: $('#username').val(),
            password: $('#password').val()
        },
        type: 'POST',
        dataType: 'json'
    });
}

if (checkPassword()) {
    // Tell the user they're logged in
}

Ten kod nie rozumie powyższych problemów asynchronicznych. W szczególności $.ajax() nie zamraża kodu podczas sprawdzania strony '/ password' na serwerze-wysyła żądanie do serwera i podczas oczekiwania natychmiast zwraca obiekt odroczony Jquery Ajax, a nie ODPOWIEDŹ z serwera. Oznacza to, że if oświadczenie zawsze będzie to odroczone obiekt, traktuj go jako true i postępuj tak, jakby użytkownik był zalogowany. Niedobrze.

Ale naprawa jest łatwa:

checkPassword()
.done(function(r) {
    if (r) {
        // Tell the user they're logged in
    } else {
        // Tell the user their password was bad
    }
})
.fail(function(x) {
    // Tell the user something bad happened
});

Nie zalecane: synchroniczne wywołania "Ajax"

Jak już wspomniałem, niektórzy(!) operacje asynchroniczne mają odpowiedniki synchroniczne. Nie jestem zwolennikiem ich użycia, ale ze względu na kompletność, oto jak wykonałbyś połączenie synchroniczne:]}

Bez jQuery

Jeśli bezpośrednio używasz XMLHTTPRequest object, pass false as trzeci argument do .open.

JQuery

Jeśli używasz jQuery , możesz ustawić opcję async na false. Zauważ, że ta opcja jest przestarzała od jQuery 1.8. Następnie możesz nadal używać wywołania zwrotnego success lub uzyskać dostęp do właściwości responseText obiektu jqXHR :

function foo() {
    var jqXHR = $.ajax({
        //...
        async: false
    });
    return jqXHR.responseText;
}

Jeśli używasz innej metody Jquery Ajax, takiej jak $.get, $.getJSON, itd., musisz go zmienić na $.ajax (ponieważ możesz przekazać tylko parametry konfiguracyjne do $.ajax).

Uwaga! nie jest możliwe wykonanie synchronicznego żądania JSONP . JSONP ze swej natury jest zawsze asynchroniczny (jeszcze jeden powód, aby nawet nie brać pod uwagę tej opcji).

 4799
Author: Felix Kling,
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-10-11 11:07:41

Jeśli Nie używasz jQuery w swoim kodzie, ta odpowiedź jest dla ciebie

Twój kod powinien być podobny do tego:

function foo() {
    var httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
    return httpRequest.responseText;
}

var result = foo(); // always ends up being 'undefined'
W 2008 roku, w ramach projektu "jQuery for AJAX", w 2009 roku, w ramach projektu "jQuery for AJAX", w 2009 roku, w ramach projektu "jQuery for AJAX", w 2009 roku, w ramach projektu "jQuery for AJAX".]}

(uwaga, dla osób korzystających z nowego fetch API, Angular lub promises dodałem kolejną odpowiedź poniżej )


Przed czym stoisz

To jest krótkie podsumowanie "wyjaśnienie problemu" z drugiej odpowiedzi, jeśli nie jesteś pewien po przeczytaniu tego, przeczytaj tamto.

A W AJAX oznacza asynchroniczny . Oznacza to, że wysłanie żądania (lub raczej otrzymanie odpowiedzi)jest usuwane z normalnego przepływu realizacji. W twoim przykładzie, .send zwraca natychmiast i następne polecenie, return result;, jest wykonywane przed wywołaniem funkcji, którą przekazałeś jako success.

Oznacza to zwracany detektor nie został jeszcze uruchomiony, co oznacza, że zwracana wartość nie została zdefiniowana.

Oto prosta analogia

function getFive(){ 
    var a;
    setTimeout(function(){
         a=5;
    },10);
    return a;
}

(Fiddle)

Zwróconą wartością a jest undefined, ponieważ część a=5 nie została jeszcze wykonana. AJAX działa w ten sposób, zwracasz wartość, zanim serwer dostał szansę powiedzieć przeglądarce, jaka jest ta wartość.

Jednym z możliwych rozwiązań tego problemu jest kodowanie ponownie aktywuj , informując swój program, co robić po zakończeniu obliczeń.

function onComplete(a){ // When the code completes, do this
    alert(a);
}

function getFive(whenDone){ 
    var a;
    setTimeout(function(){
         a=5;
         whenDone(a);
    },10);
}

To się nazywa CPS. Zasadniczo przekazujemy getFive akcję do wykonania po jej zakończeniu, mówimy naszemu kodowi, jak zareagować po zakończeniu zdarzenia (jak nasze wywołanie AJAX, lub w tym przypadku timeout).

Użycie:

getFive(onComplete);

Które powinny ostrzegać" 5 " na ekranie. (Fiddle) .

Możliwe rozwiązania

Są w zasadzie dwa sposoby rozwiązania tego problemu:

  1. wykonaj synchroniczne wywołanie AJAX(nazwijmy to SJAX).
  2. Zrestrukturyzuj swój kod, aby działał poprawnie z wywołaniami zwrotnymi.

1. Synchroniczny AJAX - nie rób tego!!

Jeśli chodzi o synchroniczny AJAX, Nie rób tego! odpowiedź Felixa budzi kilka przekonujących argumentów na temat tego, dlaczego to zły pomysł. Podsumowując, zamrozi przeglądarkę użytkownika, dopóki serwer nie zwróci odpowiedzi i nie stworzy bardzo złego doświadczenia użytkownika. Oto kolejny krótkie podsumowanie zaczerpnięte z MDN na Dlaczego:

XMLHttpRequest wspiera komunikację synchroniczną i asynchroniczną. Ogólnie jednak ze względu na wydajność należy preferować żądania asynchroniczne niż synchroniczne.

W skrócie, synchroniczne żądania blokują wykonywanie kodu... ...może to powodować poważne problemy...

Jeśli masz aby to zrobić, możesz przekazać flagę: Oto jak:

var request = new XMLHttpRequest();
request.open('GET', 'yourURL', false);  // `false` makes the request synchronous
request.send(null);

if (request.status === 200) {// That's HTTP for 'ok'
  console.log(request.responseText);
}

2. Restrukturyzacja kod

Niech twoja funkcja zaakceptuje wywołanie zwrotne. W przykładowym kodzie foo można zaakceptować wywołanie zwrotne. Będziemy mówić naszemu kodowi jak reagować kiedy foo zakończy się.

Więc:

var result = foo();
// code that depends on `result` goes here

Staje się:

foo(function(result) {
    // code that depends on `result`
});

Tutaj przekazaliśmy funkcję anonimową, ale równie łatwo mogliśmy przekazać odwołanie do istniejącej funkcji, sprawiając, że wygląda ona następująco: {24]}

function myHandler(result) {
    // code that depends on `result`
}
foo(myHandler);

Aby uzyskać więcej szczegółów na temat tego rodzaju projektowania zwrotnego, sprawdź odpowiedź Felixa.

Teraz zdefiniujmy samo foo, aby działać odpowiednio

function foo(callback) {
    var httpRequest = new XMLHttpRequest();
    httpRequest.onload = function(){ // when the request is loaded
       callback(httpRequest.responseText);// we're calling our method
    };
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
}

(fiddle)

Sprawiliśmy, że nasza funkcja foo zaakceptowała akcję do uruchomienia, gdy AJAX zakończy się pomyślnie, możemy rozszerzyć to dalej, sprawdzając, czy status odpowiedzi nie jest 200 i działając odpowiednio (Utwórz funkcję obsługi błędów i tym podobne). Skuteczne rozwiązanie naszego problemu.

Jeśli nadal masz trudności ze zrozumieniem tego przeczytaj przewodnik Ajax getting started na MDN.

 910
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
2018-03-01 01:03:52

XMLHttpRequest 2 (przede wszystkim przeczytaj odpowiedzi Benjamina Gruenbauma i Felixa Klinga)

Jeśli nie używasz jQuery i chcesz mieć ładny krótki XMLHttpRequest 2, który działa na nowoczesnych przeglądarkach, a także na przeglądarkach mobilnych, proponuję użyć go w ten sposób: {]}

function ajax(a, b, c){ // URL, callback, just a placeholder
  c = new XMLHttpRequest;
  c.open('GET', a);
  c.onload = b;
  c.send()
}

Jak widać:

  1. jest krótsza niż wszystkie inne wymienione funkcje.
  2. wywołanie zwrotne jest ustawiane bezpośrednio (więc nie ma dodatkowych niepotrzebnych zamknięć).
  3. używa nowego onload (więc nie musisz sprawdzać statusu readystate&&)
  4. są inne sytuacje, których nie pamiętam, które czynią XMLHttpRequest 1 irytującym.
Aby uzyskać odpowiedź na to wywołanie Ajax, należy skorzystać z dwóch sposobów (trzy przy użyciu nazwy var XMLHttpRequest):

Najprostszy:

this.response

Lub jeśli z jakiegoś powodu bind() wywołanie zwrotne do klasy:

e.target.response

Przykład:

function callback(e){
  console.log(this.response);
}
ajax('URL', callback);

Lub (powyższa jest lepsza funkcje anonimowe są zawsze problem):

ajax('URL', function(e){console.log(this.response)});
Nic prostszego.

Teraz niektórzy ludzie prawdopodobnie powiedzą, że lepiej jest użyć onreadystatechange lub nawet nazwy zmiennej XMLHttpRequest. To nie tak.

Sprawdź XMLHttpRequest advanced features

Obsługiwane we wszystkich * nowoczesnych przeglądarkach. I mogę potwierdzić, że używam tego podejścia, ponieważ XMLHttpRequest 2 istnieje. Nigdy nie miałem żadnego rodzaju problemu na wszystkich przeglądarkach, których używam.

Onreadystatechange jest tylko przydatne, jeśli chcesz uzyskać nagłówki na stan 2.

Użycie nazwy zmiennej XMLHttpRequest jest kolejnym dużym błędem, ponieważ musisz wykonać wywołanie zwrotne wewnątrz zamknięć onload / oreadystatechange, w przeciwnym razie go straciłeś.


Teraz, jeśli chcesz czegoś bardziej złożonego za pomocą post i FormData, możesz łatwo rozszerzyć tę funkcję:

function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val},placeholder
  c = new XMLHttpRequest;
  c.open(e||'get', a);
  c.onload = b;
  c.send(d||null)
}
Znowu ... jest to bardzo krótka funkcja, ale dostaje & post.

Przykłady użycia:

x(url, callback); // By default it's get so no need to set
x(url, callback, 'post', {'key': 'val'}); // No need to set post data

Lub przekazać pełny element formularza (document.getElementsByTagName('form')[0]):

var fd = new FormData(form);
x(url, callback, 'post', fd);

Lub ustaw kilka niestandardowych wartości:

var fd = new FormData();
fd.append('key', 'val')
x(url, callback, 'post', fd);

Jak widać nie zaimplementowałem synchronizacji... to zła rzecz.

Powiedziawszy to ... dlaczego nie zrobisz tego w łatwy sposób?

Jak wspomniano w komentarzu, użycie error & & synchronous całkowicie łamie punkt odpowiedzi. Co to jest dobry krótki sposób, aby używać Ajax w odpowiedni sposób?

obsługa błędów

function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val}, placeholder
  c = new XMLHttpRequest;
  c.open(e||'get', a);
  c.onload = b;
  c.onerror = error;
  c.send(d||null)
}

function error(e){
  console.log('--Error--', this.type);
  console.log('this: ', this);
  console.log('Event: ', e)
}
function displayAjax(e){
  console.log(e, this);
}
x('WRONGURL', displayAjax);

W powyższym skrypcie masz funkcję obsługi błędów, która jest zdefiniowana statycznie, więc nie narusza funkcji. Obsługa błędów może być również używana do innych funkcji.

Ale aby naprawdę wydostać się błąd tylko sposób jest napisać zły adres URL, w którym to przypadku każda przeglądarka rzuca błąd.

Narzędzia do obsługi błędów mogą być przydatne, jeśli ustawisz własne nagłówki, ustawisz responseType na bufor tablicy blob lub cokolwiek innego....

Nawet jeśli podasz "POSTAPAPAP" jako metodę, nie spowoduje to błędu.

Nawet jeśli zdasz 'fdggdgilfdghfldj' jako formdata nie spowoduje błędu.

W pierwszym przypadku błąd znajduje się wewnątrz displayAjax() Pod this.statusText jako Method not Allowed.

W drugim przypadku po prostu działa. Musisz sprawdzić po stronie serwera, czy przekazałeś odpowiednie dane postu.

Cross-domain not allowed wyrzuca błąd automatycznie.

W odpowiedzi na błąd nie ma kodów błędów.

Istnieje tylko this.type, który jest ustawiony na error.

Po co dodawać obsługę błędów, jeśli całkowicie nie masz kontroli nad błędami? Większość błędów jest zwracana wewnątrz tej funkcji w funkcji callback displayAjax().

Tak: nie ma potrzeby sprawdzania błędów, jeśli możesz poprawnie skopiować i wkleić adres URL. ;)

PS: jako pierwszy test napisałem x ('x', displayAjax)... i to całkowicie dostał odpowiedź...??? Więc sprawdziłem folder, w którym znajduje się HTML, i był tam plik o nazwie "x.xml". Więc nawet jeśli zapomnisz rozszerzenia pliku XMLHttpRequest 2 znajdzie go . I LOL ' D


odczyt pliku synchronicznego

Nie rób tego.

Jeśli chcesz zablokować przeglądarkę na chwilę załaduj ładny duży plik txt synchronicznie.

function omg(a, c){ // URL
  c = new XMLHttpRequest;
  c.open('GET', a, true);
  c.send();
  return c; // Or c.response
}

Teraz możesz zrobić

 var res = omg('thisIsGonnaBlockThePage.txt');
Nie ma innego sposobu, aby to zrobić w sposób nie asynchroniczny. (Tak, z pętlą setTimeout... ale poważnie?)

Kolejny punkt jest... jeśli pracujesz z API lub po prostu posiadasz pliki listy lub cokolwiek innego, zawsze używasz różnych funkcji każde żądanie...

Tylko jeśli masz stronę, na której ładujesz zawsze ten sam XML / JSON lub cokolwiek potrzebujesz tylko jednej funkcji. W takim przypadku zmodyfikuj trochę funkcję Ajax i zastąp b specjalną funkcją.


Powyższe funkcje są do podstawowego użytku.

Jeśli chcesz rozszerzyć funkcję...

Tak, możesz.

Używam wielu API i jedną z pierwszych funkcji, które integruję z każdą stroną HTML, jest pierwsza funkcja Ajax w tym odpowiedz, tylko z GET...

Ale możesz zrobić wiele rzeczy z XMLHttpRequest 2:

Zrobiłem menedżera pobierania (używając zakresów po obu stronach z CV, filereader, filesystem), różne Konwertery zmiany rozmiaru obrazu za pomocą canvas, wypełnianie baz danych websql base64images i wiele innych... Ale w takich przypadkach należy utworzyć funkcję tylko w tym celu... czasami potrzebujesz obiektu blob, buforów tablicy, możesz ustawić nagłówki, nadpisać typ MIME i jest wiele więcej...

Ale pytanie tutaj jest jak zwrócić odpowiedź Ajax... (Dodałem prosty sposób.)

 315
Author: cocco,
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-18 09:22:56

Jeśli korzystasz z obietnic, ta odpowiedź jest dla Ciebie.

Oznacza to AngularJS ,jQuery( z odroczonym), natywny zamiennik XHR( fetch), EmberJS, save BackboneJS lub dowolną bibliotekę węzłów, która zwraca promises.

Twój kod powinien być podobny do tego:

function foo() {
    var data;
    // or $.get(...).then, or request(...).then, or query(...).then
    fetch("/echo/json").then(function(response){
        data = response.json();
    });
    return data;
}

var result = foo(); // result is always undefined no matter what.

Felix Kling wykonał świetną robotę pisząc odpowiedź dla osób korzystających z jQuery z wywołaniami zwrotnymi dla AJAX. Mam odpowiedź na native XHR. Odpowiedź ta dotyczy ogólnego stosowania obietnic albo na frontend lub backend.


Zagadnienie podstawowe

Model współbieżności JavaScript w przeglądarce i na serwerze z NodeJS / io.js jest asynchroniczny i reaktywny .

Za każdym razem, gdy wywołujesz metodę, która zwraca obietnicę, then Handlery są zawsze wykonywane asynchronicznie - to znaczy po kodzie poniżej, który nie jest w .then handler.

Oznacza to, że gdy zwracasz data opiekun then masz defined nie wykonał jeszcze. To z kolei oznacza, że zwracana wartość nie została ustawiona na prawidłową wartość w czasie.

Oto prosta analogia do zagadnienia:

    function getFive(){
        var data;
        setTimeout(function(){ // set a timer for one second in the future
           data = 5; // after a second, do this
        }, 1000);
        return data;
    }
    document.body.innerHTML = getFive(); // `undefined` here and not 5

Wartość data wynosi undefined, ponieważ data = 5 część nie została jeszcze wykonana. Prawdopodobnie zostanie uruchomiony w ciągu sekundy, ale do tego czasu nie ma znaczenia dla zwracanej wartości.

Ponieważ operacja jeszcze się nie odbyła (AJAX, server call, IO, timer) zwracasz wartość przed Prośba miała szansę powiedzieć kodowi, jaka jest ta wartość.

Jednym z możliwych rozwiązań tego problemu jest ponowne aktywowanie kodu , informując swój program, co ma zrobić po zakończeniu obliczeń. Obietnice aktywnie umożliwiają to, będąc czasowym (wrażliwym na czas) w przyrodzie.

Szybkie podsumowanie obietnic

Obietnica jest wartością w czasie . Obietnice mają stan, zaczynają się jako oczekujące bez wartości i mogą się rozliczyć do:

  • spełnione , co oznacza, że obliczenia zakończyły się pomyślnie.
  • odrzucono co oznacza, że obliczenia nie powiodły się.

Obietnica może zmienić stany tylko raz , po czym zawsze pozostanie w tym samym stanie na zawsze. Możesz dołączyć then Handlery do obietnic, aby wyodrębnić ich wartość i obsłużyć błędy. then obsługa pozwalałańcuchować wywołań. Obietnice są tworzone przez za pomocą API, które zwracają them . Na przykład, bardziej nowoczesny zamiennik AJAX fetch lub obietnice powrotu jQuery $.get.

Kiedy wywołujemy .then na obietnicy i zwracamy coś z niej - otrzymujemy obietnicę dla przetworzonej wartości. Jeśli oddamy kolejną obietnicę, dostaniemy niesamowite rzeczy, ale wstrzymajmy się.

Z obietnicami

Zobaczmy, jak możemy rozwiązać powyższy problem obietnicami. Najpierw zademonstrujmy nasze rozumienie Stanów obietnic z góry przez użycie konstruktora Promise do tworzenia funkcji opóźnienia:

function delay(ms){ // takes amount of milliseconds
    // returns a new promise
    return new Promise(function(resolve, reject){
        setTimeout(function(){ // when the time is up
            resolve(); // change the promise to the fulfilled state
        }, ms);
    });
}

Teraz, po przekonwertowaniu setTimeout na użycie obietnic, możemy użyć then, aby się liczyło:

function delay(ms){ // takes amount of milliseconds
  // returns a new promise
  return new Promise(function(resolve, reject){
    setTimeout(function(){ // when the time is up
      resolve(); // change the promise to the fulfilled state
    }, ms);
  });
}

function getFive(){
  // we're RETURNING the promise, remember, a promise is a wrapper over our value
  return delay(100).then(function(){ // when the promise is ready
      return 5; // return the value 5, promises are all about return values
  })
}
// we _have_ to wrap it like this in the call site, we can't access the plain value
getFive().then(function(five){ 
   document.body.innerHTML = five;
});

Zasadniczo zamiast zwracać wartość , której nie możemy zrobić z powodu modelu współbieżności-zwracamy wrapper Dla wartości, którą możemy rozpakować za pomocą then. To jak pudełko, które można otworzyć then.

Zastosowanie to

To oznacza to samo dla twojego oryginalnego wywołania API, możesz:

function foo() {
    // RETURN the promise
    return fetch("/echo/json").then(function(response){
        return response.json(); // process it inside the `then`
    });
}

foo().then(function(response){
    // access the value inside the `then`
})
Więc to działa równie dobrze. Dowiedzieliśmy się, że nie możemy zwracać wartości z już asynchronicznych wywołań, ale możemy używać obietnic i łączyć je w łańcuchy, aby wykonać przetwarzanie. Teraz wiemy, jak zwrócić odpowiedź z połączenia asynchronicznego.

ES2015 (ES6)

ES6 wprowadza Generatory , które są funkcjami, które mogą powrócić w środku, a następnie powrócić do punktu, w którym były. To jest typowo użyteczne dla sekwencji, na przykład:

function* foo(){ // notice the star, this is ES6 so new browsers/node/io only
    yield 1;
    yield 2;
    while(true) yield 3;
}

Jest funkcją, która zwraca iterator nad sekwencją 1,2,3,3,3,3,...., która może być iteracją. Chociaż jest to interesujące samo w sobie i otwiera miejsce na wiele możliwości, jest jeden szczególny interesujący przypadek.

Jeśli sekwencja, którą tworzymy, jest sekwencją działań, a nie liczb - możemy wstrzymać funkcję za każdym razem, gdy dana akcja zostanie wywołana i poczekać na nią, zanim wznowimy funkcję. Więc zamiast sekwencja liczb, potrzebujemy sekwencji przyszłych wartości - czyli: obietnic.

Ta nieco skomplikowana, ale bardzo potężna sztuczka pozwala nam pisać kod asynchroniczny w sposób synchroniczny. Jest kilku "biegaczy", którzy robią to za Ciebie, pisanie jednego to krótkie kilka linijek kodu, ale wykracza poza zakres tej odpowiedzi. Będę używać Bluebird Promise.coroutine tutaj, ale są inne opakowania jak co lub Q.async.

var foo = coroutine(function*(){
    var data = yield fetch("/echo/json"); // notice the yield
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
});

Metoda ta zwraca samą obietnicę, którą możemy spożywać z innych koroutines. Na przykład:

var main = coroutine(function*(){
   var bar = yield foo(); // wait our earlier coroutine, it returns a promise
   // server call done here, code below executes when done
   var baz = yield fetch("/api/users/"+bar.userid); // depends on foo's result
   console.log(baz); // runs after both requests done
});
main();

ES2016 (ES7)

W ES7 jest to jeszcze bardziej znormalizowane, jest kilka propozycji w tej chwili, ale we wszystkich z nich możesz obiecać. Jest to po prostu "sugar" (ładniejsza składnia) dla powyższej propozycji ES6 poprzez dodanie słów kluczowych async i await. Wykonanie powyższego przykładu:

async function foo(){
    var data = await fetch("/echo/json"); // notice the await
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
}

Wciąż zwraca obietnicę taką samą :)

 253
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:34:51

Używasz Ajax nieprawidłowo. Chodzi o to, aby nie zwracać niczego, ale zamiast tego przekazać dane do czegoś, co nazywa się funkcją zwrotną, która obsługuje dane.

Czyli:

function handleData( responseData ) {

    // Do what you want with the data
    console.log(responseData);
}

$.ajax({
    url: "hi.php",
    ...
    success: function ( data, status, XHR ) {
        handleData(data);
    }
});

Zwrócenie czegokolwiek w programie obsługi zgłoszenia nic nie da. Zamiast tego musisz przekazać dane lub zrobić z nimi, co chcesz, bezpośrednio w funkcji sukcesu.

 199
Author: Nic,
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-11-21 14:07:03

Najprostszym rozwiązaniem jest utworzenie funkcji JavaScript i wywołanie jej dla wywołania zwrotnego Ajax success.

function callServerAsync(){
    $.ajax({
        url: '...',
        success: function(response) {

            successCallback(response);
        }
    });
}

function successCallback(responseObj){
    // Do something like read the response and show data
    alert(JSON.stringify(responseObj)); // Only applicable to JSON response
}

function foo(callback) {

    $.ajax({
        url: '...',
        success: function(response) {
           return callback(null, response);
        }
    });
}

var result = foo(function(err, result){
          if (!err)
           console.log(result);    
}); 
 190
Author: Hemant Bavle,
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-01-15 06:17:51

Odpowiem potwornie wyglądającym, ręcznie rysowanym komiksem. Drugi obrazek jest powodem, dla którego result jest undefined w twoim przykładzie kodu.

Tutaj wpisz opis obrazka

 161
Author: Johannes Fahrenkrug,
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-11 17:12:24

Angular1

Dla osób, które używają AngularJS , mogą poradzić sobie z tą sytuacją za pomocą Promises.

Tutaj jest napisane,

Obietnice mogą być używane do unnest funkcji asynchronicznych i pozwalają na łączenie wielu funkcji ze sobą.

Można znaleźć ładne Wyjaśnienie tutaj również.

Przykład znaleziony w docs wymienionych poniżej.

  promiseB = promiseA.then(
    function onSuccess(result) {
      return result + 1;
    }
    ,function onError(err) {
      //Handle error
    }
  );

 // promiseB will be resolved immediately after promiseA is resolved 
 // and its value will be the result of promiseA incremented by 1.

Angular2 i później

In Angular2 with look at the poniższy przykład, ale jego zaleca użycie Observables z Angular2.

 search(term: string) {
     return this.http
  .get(`https://api.spotify.com/v1/search?q=${term}&type=artist`)
  .map((response) => response.json())
  .toPromise();

}

Możesz to spożywać w ten sposób,

search() {
    this.searchService.search(this.searchField.value)
      .then((result) => {
    this.result = result.artists.items;
  })
  .catch((error) => console.error(error));
}

Zobacz oryginalny post tutaj. Ale Typescript nie obsługuje natywnych obietnic es6 , jeśli chcesz go użyć, możesz potrzebować do tego wtyczki.

Dodatkowo tutaj są obietnice spec zdefiniować tutaj.

 118
Author: Maleen Abewardana,
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-06 04:45:28

Większość odpowiedzi tutaj daje przydatne sugestie, gdy masz jedną operację asynchroniczną, ale czasami pojawia się, gdy musisz wykonać asynchroniczną operację dla każdego wpisu w tablicy lub innej strukturze podobnej do listy. Pokusa polega na tym, aby to zrobić:

// WRONG
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.push(result);
    });
});
console.log(results); // E.g., using them, returning them, etc.

Przykład:

// WRONG
var theArray = [1, 2, 3];
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.push(result);
    });
});
console.log("Results:", results); // E.g., using them, returning them, etc.

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

Powodem, dla którego nie działa, jest to, że wywołania zwrotne z doSomethingAsync nie uruchomiły się jeszcze do czasu, gdy próbujesz użyć wyników.

Więc jeśli masz tablicę (lub jakąś listę) i chcesz wykonywać operacje asynchroniczne dla każdego wpisu, masz dwie opcje: wykonuj operacje równolegle (nakładające się) lub szeregowo (jedna po drugiej w kolejności).

Parallel

Można uruchomić wszystkie z nich i śledzić, ile połączeń zwrotnych oczekujesz, a następnie korzystać z wyników, gdy masz już tyle połączeń zwrotnych: [51]}
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

Przykład:

var theArray = [1, 2, 3];
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(We could do away with expecting i po prostu użyj results.length === theArray.length, ale to pozostawia nam otwarte na możliwość, że theArray zostanie zmieniona, gdy połączenia są zaległe...)

Zauważ, jak używamy index Z forEach, aby zapisać wynik w results w tej samej pozycji, co wpis, do którego się odnosi, nawet jeśli wyniki są nie w porządku (ponieważ wywołania asynchroniczne niekoniecznie kończą się w kolejności, w jakiej zostały uruchomione).

Ale co jeśli musisz zwrócić te wyniki z funkcji? Jak inne odpowiedzi zaznaczono, że nie możesz; musisz mieć funkcję accept i callback (lub zwrócić obietnicę ). Oto wersja zwrotna:

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

Przykład:

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

Or here ' s a version returning a Promise instead:

function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

oczywiście, jeśli doSomethingAsync przekaże nam błędy, użyjemy reject, aby odrzucić obietnicę, gdy mamy błąd.)

Przykład:

function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(lub alternatywnie, możesz utworzyć wrapper dla doSomethingAsync, który zwraca obietnicę, a następnie wykonać poniższe czynności...)

Jeśli doSomethingAsync daje Ci obietnicę , możesz użyć Promise.all:

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(function(entry) {
        return doSomethingAsync(entry, function(result) {
            results.push(result);
        });
    }));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Przykład:

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(function(entry) {
        return doSomethingAsync(entry, function(result) {
            results.push(result);
        });
    }));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

Zauważ, że Promise.all rozwiązuje swoją obietnicę za pomocą tablicy wyników wszystkich obietnic, które mu dajesz, gdy wszystkie są rozwiązane, lub odrzuca swoją obietnicę, gdy pierwsza z obietnic, które mu dajesz / align = "left" /

Seria

Załóżmy, że nie chcesz, aby operacje były równoległe? Jeśli chcesz je uruchamiać jeden po drugim, musisz poczekać na zakończenie każdej operacji przed rozpoczęciem następnej. Oto przykład funkcji, która to robi i wywołuje callback z wynikiem:

function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

(ponieważ wykonujemy pracę szeregowo, możemy po prostu użyć results.push(result), ponieważ wiemy, że nie dostaniemy wyników niezgodnych z porządkiem. W powyższym mogliśmy użyć results[index] = result;, ale w niektórych poniższe przykłady nie mają indeksu do użycia.)

Przykład:

function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(lub, ponownie, zbuduj opakowanie dla doSomethingAsync, które daje obietnicę i wykonaj poniższe czynności...)

Jeśli doSomethingAsync daje Ci obietnicę, jeśli możesz użyć składni ES2017+ (być może z transpilerem takim jak Babel ), możesz użyćasync function with for-of oraz await:

async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

Przykład:

async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

Jeśli nie możesz użyć składni ES2017+ (jeszcze), możesz użyć wariacji wzorca "Promise reduce" (jest to bardziej złożone niż zwykle Promise reduce, ponieważ nie przekazujemy wyniku z jednego do drugiego, ale zamiast tego gromadzimy ich wyniki w tablicy):

function doSomethingWith(theArray) {
    return theArray.reduce(function(p, entry) {
        return p.then(function(results) {
            return doSomethingAsync(entry).then(function(result) {
                results.push(result);
                return results;
            });
        });
    }, Promise.resolve([]));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Przykład:

function doSomethingWith(theArray) {
    return theArray.reduce(function(p, entry) {
        return p.then(function(results) {
            return doSomethingAsync(entry).then(function(result) {
                results.push(result);
                return results;
            });
        });
    }, Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

...co jest mniej uciążliwe z ES2015 + funkcje strzałek :

function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

Przykład:

function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}
 94
Author: T.J. Crowder,
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-09-13 04:34:11

Spójrz na ten przykład:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope,$http) {

    var getJoke = function(){
        return $http.get('http://api.icndb.com/jokes/random').then(function(res){
            return res.data.value;  
        });
    }

    getJoke().then(function(res) {
        console.log(res.joke);
    });
});

Jak widać getJoke jest zwracaniem rozwiązanej obietnicy (jest rozwiązana po powrocie res.data.value). Więc czekasz do $http.get żądanie jest zakończone, a następnie console.log (res.joke) jest wykonywany (jako normalny przepływ asynchroniczny).

To jest plnkr:

Http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/

 77
Author: Francisco Carmona,
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-01-13 03:55:15

Innym sposobem zwracania wartości z funkcji asynchronicznej jest przekazanie w obiekcie, który będzie przechowywał wynik z funkcji asynchronicznej.

Oto przykład tego samego:

var async = require("async");

// This wires up result back to the caller
var result = {};
var asyncTasks = [];
asyncTasks.push(function(_callback){
    // some asynchronous operation
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;
            _callback();
        }
    });
});

async.parallel(asyncTasks, function(){
    // result is available after performing asynchronous operation
    console.log(result)
    console.log('Done');
});

Używam obiektu result do przechowywania wartości podczas operacji asynchronicznej. Dzięki temu wynik będzie dostępny nawet po wykonaniu zadania asynchronicznego.

Często używam tego podejścia. Byłbym zainteresowany, aby wiedzieć, jak dobrze to podejście działa, gdzie okablowanie wynik z powrotem poprzez kolejne moduły jest zaangażowany.
 71
Author: jsbisht,
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-12-17 12:55:25

Jest to jedno z miejsc, w których dwa sposoby wiązania danych używane w wielu nowych frameworkach JavaScript będą dla ciebie bardzo przydatne...

Więc jeśli używaszAngular, React lub innych frameworków, które działają na dwa sposoby, ten problem jest po prostu dla Ciebie naprawiony, więc w prostym słowie, Twój wynik jest undefined na pierwszym etapie, więc masz result = undefined zanim otrzymasz dane, to jak tylko otrzymasz wynik, zostanie on zaktualizowany i zostanie przypisany do nowej wartości. który jest odpowiedzią na twoje wezwanie Ajax...

Ale jak można to zrobić w pure javascript lub jQuery na przykład, jak zadałeś w tym pytaniu?

Możesz użyć callback, promise i ostatnio observable , aby obsłużyć go za Ciebie, na przykład w promises mamy jakąś funkcję, taką jak success () lub then (), która zostanie wykonana, gdy Twoje dane będą gotowe, tak samo z funkcją callback lub subscribe na obserwowalne.

Na przykład w Twoim przypadku, którego używasz jQuery, możesz zrobić coś takiego:

$(document).ready(function(){
    function foo() {
        $.ajax({url: "api/data", success: function(data){
            fooDone(data); //after we have data, we pass it to fooDone
        }});
    };

    function fooDone(data) {
        console.log(data); //fooDone has the data and console.log it
    };

    foo(); //call happens here
});

Aby uzyskać więcej informacji na tematpromises iobservables , które są nowszymi sposobami wykonywania tych asynchronicznych rzeczy.

 64
Author: Alireza,
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-25 13:46:01

Podczas gdy obietnice i wezwania działają dobrze w wielu sytuacjach, jest to ból z tyłu, aby wyrazić coś w stylu: {]}

if (!name) {
  name = async1();
}
async2(name);

Skończysz przechodząc przez async1; Sprawdź, czy name jest niezdefiniowany, czy nie i zadzwoń do połączenia zwrotnego odpowiednio.

async1(name, callback) {
  if (name)
    callback(name)
  else {
    doSomething(callback)
  }
}

async1(name, async2)

Podczas gdy jest to okay w małych przykładach robi się irytujące, gdy masz wiele podobnych przypadków i obsługi błędów zaangażowanych.

Fibers pomaga w rozwiązaniu problemu.

var Fiber = require('fibers')

function async1(container) {
  var current = Fiber.current
  var result
  doSomething(function(name) {
    result = name
    fiber.run()
  })
  Fiber.yield()
  return result
}

Fiber(function() {
  var name
  if (!name) {
    name = async1()
  }
  async2(name)
  // Make any number of async calls from here
}

Możesz sprawdzić projekt tutaj .

 62
Author: rohithpr,
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-05-09 13:02:40

Krótka odpowiedź brzmi: musisz zaimplementować wywołanie zwrotne takie jak:

function callback(response) {
    // Here you can do what ever you want with the response object.
    console.log(response);
}

$.ajax({
    url: "...",
    success: callback
});
 59
Author: Pablo Matias Gomez,
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-09 18:32:41

Poniższy przykład, który napisałem pokazuje, jak

  • Obsługa asynchronicznych wywołań HTTP;
  • Wait for response from every API call;
  • Use Promise pattern;
  • Użyj Obietnicy.All pattern to join multiple HTTP calls;

Ten przykład pracy jest samowystarczalny. Zdefiniuje prosty obiekt request, który używa obiektu window XMLHttpRequest do wykonywania wywołań. Zdefiniuje prostą funkcję czekania na kilka obietnic, które będą zakończone.

Kontekst. Przykładem jest odpytywanie punktu końcowego Spotify Web API w celu wyszukania obiektów playlist dla danego zestawu ciągów zapytań:

[
 "search?type=playlist&q=%22doom%20metal%22",
 "search?type=playlist&q=Adele"
]

Dla każdego elementu, Nowa obietnica uruchomi blok- ExecutionBlock, przeanalizuje wynik, zaplanuje nowy zestaw obietnic na podstawie tablicy wyników, czyli listy obiektów Spotify user i wykona nowe wywołanie HTTP w ExecutionProfileBlock asynchronicznie.

Można wtedy zobaczyć zagnieżdżoną strukturę obietnicy, która pozwala odtwarza wiele całkowicie asynchronicznych zagnieżdżonych wywołań HTTP i dołącza wyniki z każdego podzbioru wywołań przez Promise.all.

Uwaga Najnowsze interfejsy API Spotifysearch wymagają podania tokena dostępu w nagłówkach żądań:

-H "Authorization: Bearer {your access token}" 

Tak więc, aby uruchomić poniższy przykład, musisz umieścić swój token dostępu w nagłówkach żądań:

var spotifyAccessToken = "YourSpotifyAccessToken";
var console = {
    log: function(s) {
        document.getElementById("console").innerHTML += s + "<br/>"
    }
}

// Simple XMLHttpRequest
// based on https://davidwalsh.name/xmlhttprequest
SimpleRequest = {
    call: function(what, response) {
        var request;
        if (window.XMLHttpRequest) { // Mozilla, Safari, ...
            request = new XMLHttpRequest();
        } else if (window.ActiveXObject) { // Internet Explorer
            try {
                request = new ActiveXObject('Msxml2.XMLHTTP');
            }
            catch (e) {
                try {
                  request = new ActiveXObject('Microsoft.XMLHTTP');
                } catch (e) {}
            }
        }

        // State changes
        request.onreadystatechange = function() {
            if (request.readyState === 4) { // Done
                if (request.status === 200) { // Complete
                    response(request.responseText)
                }
                else
                    response();
            }
        }
        request.open('GET', what, true);
        request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken);
        request.send(null);
    }
}

//PromiseAll
var promiseAll = function(items, block, done, fail) {
    var self = this;
    var promises = [],
                   index = 0;
    items.forEach(function(item) {
        promises.push(function(item, i) {
            return new Promise(function(resolve, reject) {
                if (block) {
                    block.apply(this, [item, index, resolve, reject]);
                }
            });
        }(item, ++index))
    });
    Promise.all(promises).then(function AcceptHandler(results) {
        if (done) done(results);
    }, function ErrorHandler(error) {
        if (fail) fail(error);
    });
}; //promiseAll

// LP: deferred execution block
var ExecutionBlock = function(item, index, resolve, reject) {
    var url = "https://api.spotify.com/v1/"
    url += item;
    console.log( url )
    SimpleRequest.call(url, function(result) {
        if (result) {

            var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) {
                return item.owner.href;
            })
            resolve(profileUrls);
        }
        else {
            reject(new Error("call error"));
        }
    })
}

arr = [
    "search?type=playlist&q=%22doom%20metal%22",
    "search?type=playlist&q=Adele"
]

promiseAll(arr, function(item, index, resolve, reject) {
    console.log("Making request [" + index + "]")
    ExecutionBlock(item, index, resolve, reject);
}, function(results) { // Aggregated results

    console.log("All profiles received " + results.length);
    //console.log(JSON.stringify(results[0], null, 2));

    ///// promiseall again

    var ExecutionProfileBlock = function(item, index, resolve, reject) {
        SimpleRequest.call(item, function(result) {
            if (result) {
                var obj = JSON.parse(result);
                resolve({
                    name: obj.display_name,
                    followers: obj.followers.total,
                    url: obj.href
                });
            } //result
        })
    } //ExecutionProfileBlock

    promiseAll(results[0], function(item, index, resolve, reject) {
        //console.log("Making request [" + index + "] " + item)
        ExecutionProfileBlock(item, index, resolve, reject);
    }, function(results) { // aggregated results
        console.log("All response received " + results.length);
        console.log(JSON.stringify(results, null, 2));
    }

    , function(error) { // Error
        console.log(error);
    })

    /////

  },
  function(error) { // Error
      console.log(error);
  });
<div id="console" />

Szeroko omówiłem To rozwiązanie tutaj.

 53
Author: loretoparisi,
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-30 09:33:11

2017 odpowiedź: możesz teraz robić dokładnie to, co chcesz w każdej bieżącej przeglądarce i węźle

To dość proste:

  • Return A Promise
  • Skrypt JavaScript wyświetla informację, że obietnica zostanie rozwiązana w postaci wartości (np. odpowiedź HTTP)
  • Dodaj 'async' do funkcji nadrzędnej

Oto działająca wersja twojego kodu:

(async function(){

var response = await superagent.get('...')
console.log(response)

})()

Oczekiwanie jest obsługiwane we wszystkich aktualnych przeglądarkach i węzłach 8

 49
Author: mikemaccana,
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-10-01 10:06:17

Możesz użyć tej niestandardowej biblioteki (napisanej przy użyciu obietnicy), aby wykonać zdalne połączenie.

function $http(apiConfig) {
    return new Promise(function (resolve, reject) {
        var client = new XMLHttpRequest();
        client.open(apiConfig.method, apiConfig.url);
        client.send();
        client.onload = function () {
            if (this.status >= 200 && this.status < 300) {
                // Performs the function "resolve" when this.status is equal to 2xx.
                // Your logic here.
                resolve(this.response);
            }
            else {
                // Performs the function "reject" when this.status is different than 2xx.
                reject(this.statusText);
            }
        };
        client.onerror = function () {
            reject(this.statusText);
        };
    });
}

Prosty przykład użycia:

$http({
    method: 'get',
    url: 'google.com'
}).then(function(response) {
    console.log(response);
}, function(error) {
    console.log(error)
});
 48
Author: Vinoth Rajendran,
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-12-17 10:59:13

Innym rozwiązaniem jest wykonywanie kodu za pomocą sekwencyjnego executora nsynjs .

Jeśli funkcja bazowa jest promisified

Nsynjs oceni wszystkie obietnice sekwencyjnie i umieści wynik obietnicy wdata własności:

function synchronousCode() {

    var getURL = function(url) {
        return window.fetch(url).data.text().data;
    };
    
    var url = 'https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
    console.log('received bytes:',getURL(url).length);
    
};

nsynjs.run(synchronousCode,{},function(){
    console.log('synchronousCode done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>

Jeśli funkcja bazowa nie jest promisified

Krok 1. Funkcja Wrap z wywołaniem zwrotnym do nsynjs-aware wrapper (jeśli ma promisified version, możesz pominąć ten test):

var ajaxGet = function (ctx,url) {
    var res = {};
    var ex;
    $.ajax(url)
    .done(function (data) {
        res.data = data;
    })
    .fail(function(e) {
        ex = e;
    })
    .always(function() {
        ctx.resume(ex);
    });
    return res;
};
ajaxGet.nsynjsHasCallback = true;

Krok 2. Put logika synchroniczna do funkcji:

function process() {
    console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);
}
Krok 3. Uruchom funkcję w sposób synchroniczny poprzez nnsynjs:
nsynjs.run(process,this,function () {
    console.log("synchronous function finished");
});

Nsynjs będzie oceniać wszystkie operatory i wyrażenia krok po kroku, wstrzymując wykonanie w przypadku, gdy wynik jakiejś wolnej funkcji nie jest gotowy.

Więcej przykładów tutaj: https://github.com/amaksr/nsynjs/tree/master/examples

 43
Author: amaksr,
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-04 20:30:36

Js jest pojedynczym gwintem.

Przeglądarkę można podzielić na trzy części:

1) Pętla Zdarzeń

2) Web API

3)Kolejka Zdarzeń

Pętla zdarzeń działa na zawsze, tzn. w rodzaju pętli nieskończonej.Kolejka zdarzeń to miejsce, w którym wszystkie funkcje są wypychane na jakieś zdarzenie (przykład:click) jest to jedna po drugiej wykonywane z kolejki i wprowadzane do pętli zdarzeń, które wykonują tę funkcję i przygotowują ją do następnej po wykonaniu pierwszej.Oznacza to wykonanie jednego funkcja nie uruchamia się, dopóki funkcja przed nią w kolejce nie zostanie wykonana w pętli zdarzeń.

Pomyślmy teraz, że wprowadziliśmy dwie funkcje w kolejce, jedna służy do pobierania danych z serwera, a druga wykorzystuje te dane.Wypchnęliśmy najpierw funkcję serverRequest() w queue, a następnie funkcję utiliseData (). funkcja serverRequest przechodzi w pętlę zdarzeń i wykonuje połączenie z serwerem, ponieważ nigdy nie wiemy, ile czasu zajmie uzyskanie danych z serwera proces ten ma więc zająć trochę czasu i dlatego zajęliśmy nasza pętla zdarzeń w ten sposób zawieszamy naszą stronę, gdzie Web API wchodzi w rolę bierze tę funkcję z pętli zdarzeń i zajmuje się tworzeniem pętli zdarzeń przez serwer, dzięki czemu możemy wykonać następną funkcję z kolejki.Następną funkcją w kolejce jest utiliseData (), która przechodzi w pętlę, ale z powodu braku dostępnych danych traci się i wykonywanie następnej funkcji trwa do końca kolejki.(To się nazywa asynchroniczne wywołanie, czyli możemy zrobić coś innego, dopóki nie dostaniemy danych)

Załóżmy, że nasz serverRequest() funkcja miała w kodzie instrukcję return, kiedy odzyskamy dane z serwera Web API wypchnie je do kolejki na końcu kolejki. Gdy zostanie on wypchnięty na końcu kolejki, nie możemy wykorzystać jego danych, ponieważ w kolejce nie ma żadnej funkcji, która mogłaby wykorzystać te dane. nie jest więc możliwe zwrócenie czegoś z asynchronicznego wywołania.

Zatem rozwiązaniem tego problemu jest odpowiedź zwrotna lub obietnica .

obraz z jednej z odpowiedzi tutaj, poprawnie wyjaśnia callback użyj... Przekazujemy naszą funkcję (funkcję wykorzystującą dane zwracane z serwera) do funkcji wywołującej serwer.

CallBack

 function doAjax(callbackFunc, method, url) {
  var xmlHttpReq = new XMLHttpRequest();
  xmlHttpReq.open(method, url);
  xmlHttpReq.onreadystatechange = function() {

      if (xmlHttpReq.readyState == 4 && xmlHttpReq.status == 200) {
        callbackFunc(xmlHttpReq.responseText);
      }


  }
  xmlHttpReq.send(null);

}

W moim kodzie nazywa się to

function loadMyJson(categoryValue){
  if(categoryValue==="veg")
  doAjax(print,"GET","http://localhost:3004/vegetables");
  else if(categoryValue==="fruits")
  doAjax(print,"GET","http://localhost:3004/fruits");
  else 
  console.log("Data not found");
}

Przeczytaj tutaj, aby poznać nowe metody w ECMA(2016/17) do wykonywania połączeń asynchronicznych (odpowiedź @ Felix Kling na górze) https://stackoverflow.com/a/14220323/7579856

 42
Author: Aniket Jha,
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-03-16 16:48:55

Oto kilka podejść do pracy z żądaniami asynchronicznymi:

  1. Browser Promise object
  2. Q - biblioteka obietnic dla JavaScript
  3. A + Obiecuje.js
  4. jQuery deferred
  5. XMLHttpRequest API
  6. wykorzystanie koncepcji callback - jako implementacji w pierwszej odpowiedzi

Przykład: implementacja jQuery odroczona do pracy z wieloma żądaniami

var App = App || {};

App = {
    getDataFromServer: function(){

      var self = this,
                 deferred = $.Deferred(),
                 requests = [];

      requests.push($.getJSON('request/ajax/url/1'));
      requests.push($.getJSON('request/ajax/url/2'));

      $.when.apply(jQuery, requests).done(function(xhrResponse) {
        return deferred.resolve(xhrResponse.result);
      });
      return deferred;
    },

    init: function(){

        this.getDataFromServer().done(_.bind(function(resp1, resp2) {

           // Do the operations which you wanted to do when you
           // get a response from Ajax, for example, log response.
        }, this));
    }
};
App.init();
 29
Author: Mohan Dere,
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-06-22 09:31:40

Jest to bardzo częsty problem, z którym borykamy się zmagając się z "tajemnicami" JavaScript. Pozwól, że spróbuję dziś wyjaśnić tę tajemnicę.

Zacznijmy od prostej funkcji JavaScript:

function foo(){
// do something 
 return 'wohoo';
}

let bar = foo(); // bar is 'wohoo' here

Jest to proste wywołanie funkcji synchronicznej (gdzie każda linia kodu jest 'skończona swoim zadaniem' przed następną sekwencją), a wynik jest taki sam, jak oczekiwano.

Teraz dodajmy trochę zwrotów, wprowadzając małe opóźnienie w naszej funkcji, tak aby wszystkie linie kodu nie są "skończone" w kolejności. W ten sposób emuluje asynchroniczne zachowanie funkcji:

function foo(){
 setTimeout( ()=>{
   return 'wohoo';
  }, 1000 )
}

let bar = foo() // bar is undefined here

No i proszę, to opóźnienie zepsuło funkcjonalność, której się spodziewaliśmy! Ale co dokładnie się stało ? To całkiem logiczne, jeśli spojrzysz na kod. Funkcja foo() Po wykonaniu niczego nie zwraca (zwracana wartość to undefined), ale uruchamia timer, który wykonuje funkcję po 1s, aby zwrócić 'wohoo'. Ale jak widać, wartość, która jest przypisana do bar jest natychmiast zwrócił rzeczy z foo (), a nie wszystko inne, co przychodzi później.

Jak zatem rozwiązać ten problem?

Zapytajmy naszą funkcję o obietnicę. Obietnica jest tak naprawdę o tym, co to znaczy: oznacza to, że funkcja gwarantuje dostarczenie dowolnego wyjścia, które otrzyma w przyszłości. więc zobaczmy to w akcji dla naszego małego problemu powyżej :

function foo(){
   return new Promise( (resolve, reject) => { // I want foo() to PROMISE me something
    setTimeout ( function(){ 
      // promise is RESOLVED , when execution reaches this line of code
       resolve('wohoo')// After 1 second, RESOLVE the promise with value 'wohoo'
    }, 1000 )
  })
}

let bar ; 
foo().then( res => {
 bar = res;
 console.log(bar) // will print 'wohoo'
});

Tak więc, podsumowanie jest-do rozwiązania funkcji asynchronicznych, takich jak wywołania ajax oparte itp., możesz użyj obietnicy resolve wartości (którą zamierzasz zwrócić). Tak więc, w skrócie ty rozwiązujesz wartość zamiast zwracając, w funkcjach asynchronicznych.

[[34]}UPDATE (Promises with async / wait)

Oprócz używania then/catch do pracy z obietnicami, istnieje jeszcze jedno podejście. Chodzi o to, aby rozpoznać funkcję asynchroniczną, a następnie poczekać na rozwiązanie obietnic, zanim przejdziemy do następnej linii kodu. To wciąż tylko promises pod kaptur, ale z innym podejściem składniowym. Aby wszystko było jaśniejsze, można znaleźć porównanie poniżej:

Then / catch version:

function fetchUsers(){
   let users = [];
   getUsers()
   .then(_users => users = _users)
   .catch(err =>{
      throw err
   })
   return users;
 }

Async / wersja oczekująca:

  async function fetchUsers(){
     try{
        let users = await getUsers()
        return users;
     }
     catch(err){
        throw err;
     }
  }
 29
Author: Anish K.,
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-10-09 17:33:32

Użyj funkcji callback() wewnątrz sukcesu foo(). Spróbuj w ten sposób. Jest to proste i łatwe do zrozumienia.  

var lat = "";
var lon = "";
function callback(data) {
    lat = data.lat;
    lon = data.lon;
}
function getLoc() {
    var url = "http://ip-api.com/json"
    $.getJSON(url, function(data) {
        callback(data);
    });
}

getLoc();
 27
Author: Mahfuzur Rahman,
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-11-17 09:15:45

ECMAScript 6 posiada 'generatory', które pozwalają na łatwe programowanie w stylu asynchronicznym.

function* myGenerator() {
    const callback = yield;
    let [response] = yield $.ajax("https://stackoverflow.com", {complete: callback});
    console.log("response is:", response);

    // examples of other things you can do
    yield setTimeout(callback, 1000);
    console.log("it delayed for 1000ms");
    while (response.statusText === "error") {
        [response] = yield* anotherGenerator();
    }
}

Aby uruchomić powyższy kod należy wykonać następujące czynności:

const gen = myGenerator(); // Create generator
gen.next(); // Start it
gen.next((...args) => gen.next([...args])); // Set its callback function

Jeśli chcesz kierować przeglądarki, które nie obsługują ES6, możesz uruchomić kod przez Babel lub closure-compiler, aby wygenerować ECMAScript 5.

Wywołania zwrotne ...args są zawijane w tablicę i niszczone podczas ich odczytywania, tak aby wzorzec mógł poradzić sobie z wywołaniami zwrotnymi, które mają wiele argumentów. Na przykład z węzłem fs :

const [err, data] = yield fs.readFile(filePath, "utf-8", callback);
 27
Author: James,
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-07-31 10:35:51

Krótka odpowiedź : Twoja metoda foo() powraca natychmiast, podczas gdy $ajax() jest wykonywana asynchronicznie po tym, jak funkcja zwraca . Problem polega na tym, jak lub gdzie przechowywać wyniki pobrane przez asynchroniczne wywołanie po jego powrocie.

W tym wątku podano kilka rozwiązań. Być może najprostszym sposobem jest przekazanie obiektu do metody foo() i zapisanie wyników w elemencie tego obiektu po zakończeniu wywołania asynchronicznego.

function foo(result) {
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;   // Store the async result
        }
    });
}

var result = { response: null };   // Object to hold the async result
foo(result);                       // Returns before the async completes

Zauważ, że call to foo() nadal nie zwróci nic użytecznego. Jednak wynik wywołania asynchronicznego będzie teraz przechowywany w result.response.

 25
Author: David R Tribble,
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-09-23 22:52:03

Oczywiście istnieje wiele podejść, takich jak synchroniczne żądanie, obietnica, ale z mojego doświadczenia myślę, że powinieneś użyć podejścia zwrotnego. Naturalne jest asynchroniczne zachowanie Javascript. Tak więc twój fragment kodu można przepisać trochę inaczej:

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            myCallback(response);
        }
    });

    return result;
}

function myCallback(response) {
    // Does something.
}
 17
Author: Khoa Bui,
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-03-07 14:23:16

Pytanie brzmiało:

Jak zwrócić odpowiedź z połączenia asynchronicznego?

Które można interpretować jako:

Jak sprawić, by asynchroniczny wygląd kodu synchroniczny ?

Rozwiązaniem będzie unikanie wywołań zwrotnych i użycie kombinacji obietnic i asynchronicznych / oczekujących.

Chciałbym podać przykład żądania Ajax.

(chociaż może być napisany w Javascript, I preferuje pisanie w Pythonie i kompilowanie do Javascript przy użyciu transkrypt. To będzie wystarczająco jasne.)

Pozwala najpierw włączyć użycie JQuery, aby $ było dostępne jako S:

__pragma__ ('alias', 'S', '$')

Zdefiniuj funkcję, która zwraca obietnicę , w tym przypadku wywołanie Ajax:

def read(url: str):
    deferred = S.Deferred()
    S.ajax({'type': "POST", 'url': url, 'data': { },
        'success': lambda d: deferred.resolve(d),
        'error': lambda e: deferred.reject(e)
    })
    return deferred.promise()

Użyj asynchronicznego kodu tak, jakby był synchroniczny :

async def readALot():
    try:
        result1 = await read("url_1")
        result2 = await read("url_2")
    except Exception:
        console.warn("Reading a lot failed")
 17
Author: Pieter Jan Bonestroo,
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:36:33

Znajdujemy się we wszechświecie, który zdaje się rozwijać w wymiarze, który nazywamy "czasem". Tak naprawdę nie rozumiemy, czym jest czas, ale opracowaliśmy abstrakcje i słownictwo, które pozwalają nam rozumować i mówić o nim: "przeszłość", "teraźniejszość", "przyszłość", "przed", "po".

Systemy komputerowe, które budujemy-coraz więcej-mają czas jako ważny wymiar. Pewne rzeczy są ustawione, aby wydarzyć się w przyszłości. Potem inne rzeczy muszą się wydarzyć po tych pierwszych rzeczach w końcu występuje. Jest to podstawowe pojęcie zwane "asynchronicznością". W naszym coraz bardziej sieciowym świecie, najczęstszym przypadkiem asynchoniczności jest oczekiwanie na jakiś zdalny system, który odpowie na jakieś żądanie.

Rozważ przykład. Zadzwoń do mleczarza i zamów mleko. Kiedy przyjdzie, chcesz dodać to do kawy. Nie możesz teraz dodawać mleka do kawy, bo jeszcze jej nie ma. Musisz poczekać, aż nadejdzie, zanim włożysz go do kawy. Innymi słowy, następujące nie działa:

var milk = order_milk();
put_in_coffee(milk);

Ponieważ JS nie ma sposobu, aby wiedzieć, że musiczekać na order_milk, aby zakończyć, zanim wykona put_in_coffee. Innymi słowy, nie wie, że order_milkjest asynchronicznym - jest czymś, co nie spowoduje mleka do pewnego przyszłego czasu. JS i inne języki deklaratywne wykonują jedno polecenie po drugim bez czekania.

Klasyczne podejście JS do tego problemu, wykorzystując fakt, że JS obsługuje funkcje jako obiekty pierwszej klasy, które mogą być przekazywane, to przekazanie funkcji jako parametru do asynchronicznego żądania, które następnie wywoła, gdy zakończy swoje zadanie kiedyś w przyszłości. To jest podejście "zwrotne". Wygląda to tak:

order_milk(put_in_coffee);

order_milk startuje, zamawia mleko, wtedy, kiedy i tylko wtedy, gdy dociera, wywołuje put_in_coffee.

Problem z tym podejściem zwrotnym polega na tym, że zanieczyszcza normalną semantykę funkcji raportującej jej wynik za pomocą return; zamiast tego, funkcje muszą nost raportuje swoje wyniki przez wywołanie wywołania zwrotnego podanego jako parametr. Również takie podejście może szybko stać się nieporęczne, gdy mamy do czynienia z dłuższymi sekwencjami zdarzeń. Powiedzmy na przykład, że chcę poczekać, aż mleko zostanie umieszczone w kawie, a następnie i tylko wtedy wykonać trzeci krok, a mianowicie wypicie kawy. W końcu muszę napisać coś takiego:

order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }

Gdzie przechodzę do put_in_coffee zarówno mleka do włożenia, jak i działania (drink_coffee) do wykonać po włożeniu mleka. Taki kod staje się trudny do napisania, odczytania i debugowania.

W tym przypadku możemy przepisać kod w pytaniu jako:

var answer;
$.ajax('/foo.json') . done(function(response) {
  callback(response.data);
});

function callback(data) {
  console.log(data);
}

Wpisz obietnice

To była motywacja dla pojęcia "obietnicy", która jest szczególnym rodzajem wartości, która reprezentuje przyszłość lub asynchroniczny wynik pewnego rodzaju. Może reprezentować coś, co już się wydarzyło lub co wydarzy się w przyszłości, lub może nigdy się to nie zdarza. Obietnice mają jedną metodę o nazwie then, do której przekazujesz akcję do wykonania, gdy wynik, który reprezentuje obietnica, został zrealizowany. W przypadku naszego mleka i kawy projektujemy order_milk, aby zwrócić obietnicę dotarcia mleka, a następnie określić put_in_coffee jako działanie then w następujący sposób: {[35]]}
order_milk() . then(put_in_coffee)
Jedną z zalet tego jest to, że możemy połączyć je ze sobą, aby utworzyć sekwencje przyszłych zdarzeń ("łańcuchowanie"):]}
order_milk() . then(put_in_coffee) . then(drink_coffee)

Zastosujmy obiecuje twój konkretny problem. Będziemy zawijać naszą logikę żądania wewnątrz funkcji, która zwraca obietnicę: {]}

function get_data() {
  return $.ajax('/foo.json');
}

Właściwie, wszystko co zrobiliśmy to dodanie return do połączenia do $.ajax. To działa, ponieważ jQuery $.ajax już zwraca coś w rodzaju obietnicy. (W praktyce, bez wdawania się w szczegóły, wolelibyśmy zawinąć to wywołanie, aby zwrócić prawdziwą obietnicę, lub użyć jakiejś alternatywy dla {27]}, która to robi.) Teraz, jeśli chcemy załadować plik i poczekać aż skończy się i następnie zrób coś, możemy po prostu powiedzieć

get_data() . then(do_something)

Na przykład,

get_data() . 
  then(function(data) { console.log(data); });

Gdy używamy obietnic, przekazujemy wiele funkcji do then, więc często pomocne jest użycie bardziej kompaktowych funkcji strzałek w stylu ES6:

get_data() . 
  then(data => console.log(data));

Słowo kluczowe async

Ale nadal jest coś niejasno niezadowolonego z konieczności pisania kodu w jeden sposób, jeśli synchroniczny i całkiem inny sposób, jeśli asynchroniczny. Dla synchronizacji zapisujemy

a();
b();

Ale jeśli a jest asynchroniczne, z obietnicami musimy napisać

a() . then(b);

Powyżej, powiedzieliśmy: "JS nie ma sposobu, aby wiedzieć, że musi czekać na pierwsze wywołanie, aby zakończyć, zanim wykona drugie". Czy nie byłoby miło, gdyby był jakiś sposób, żeby powiedzieć to JS? Okazuje się, że istnieje--słowo kluczowe await, używane wewnątrz specjalnego typu funkcji zwanej funkcją "asynchroniczną". Ta funkcja jest częścią nadchodzącej wersji ES, ale jest już dostępna w transpilerach, takich jak Babel given odpowiednie ustawienia. To pozwala nam po prostu napisać

async function morning_routine() {
  var milk   = await order_milk();
  var coffee = await put_in_coffee(milk);
  await drink(coffee);
}

W Twoim przypadku mógłbyś napisać coś w stylu

async function foo() {
  data = await get_data();
  console.log(data);
}
 15
Author: 3 revs, 2 users 95%user663031,
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-11-09 20:30:58

Używając ES2017 powinieneś mieć to jako deklarację funkcji

async function foo() {
    var response = await $.ajax({url: '...'})
    return response;
}

I wykonując to w ten sposób.

(async function() {
    try {
        var result = await foo()
        console.log(result)
    } catch (e) {}
})()

Lub składnia obietnicy

foo().then(response => {
    console.log(response)

}).catch(error => {
    console.log(error)

})
 10
Author: Fernando Carvajal,
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-01-24 06:18:55

Najpierw zobaczmy Las, zanim spojrzymy na drzewa.

Jest tu wiele pouczających odpowiedzi ze świetnymi szczegółami, żadnej z nich nie będę powtarzał. Kluczem do programowania w JavaScript jest posiadanie najpierw poprawnego modelu mentalnego ogólnego wykonania.

  1. twoje punkty wejścia są wykonywane w wyniku zdarzenia. Na przykład: znacznik skryptu z kodem jest ładowany do przeglądarki. (W związku z tym, dlatego może być konieczne, aby zająć się gotowość do strona do uruchomienia kodu, jeśli wymaga elementów dom do zbudowania w pierwszej kolejności itp.)
  2. Twój kod jest wykonywany do końca--jakkolwiek wiele asynchronicznych wywoła go sprawia,że -- bez wykonywania żadnych Twoich wywołań zwrotnych, w tym XHR requests, set timeouts, Dom event handlers, itp. Każde z tych wywołań zwrotnych oczekujących na wykonanie będzie siedzieć w kolejce, czekając na swoją kolej, aby zostać uruchomione po innych zdarzeniach, które zostały uruchomione.
  3. każde wywołanie zwrotne na żądanie XHR, Ustaw timeout lub dom wywołane zdarzenie zostanie zakończone.

Dobrą wiadomością jest to, że jeśli dobrze zrozumiesz ten punkt, nigdy nie będziesz musiał martwić się o warunki wyścigu. Powinieneś przede wszystkim powiedzieć, jak chcesz zorganizować swój kod jako odpowiedź na różne dyskretne zdarzenia i jak chcesz połączyć je razem w logiczną sekwencję. Możesz użyć obietnic lub wyższego poziomu new async / wait jako narzędzi do tego celu, lub możesz przewrócić swój własne.

Ale nie powinieneś używać żadnych narzędzi taktycznych, aby rozwiązać problem, dopóki nie poczujesz się komfortowo z rzeczywistą domeną problemu. Narysuj mapę tych zależności, aby wiedzieć, co trzeba uruchomić, kiedy. Próba doraźnego podejścia do wszystkich tych wywołań zwrotnych nie będzie Ci dobrze służyć.

 9
Author: Haim Zamir,
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-29 08:36:37

Zamiast rzucać w ciebie kodem, istnieją 2 koncepcje, które są kluczem do zrozumienia, jak JS obsługuje wywołań zwrotnych i asynchroniczność. (czy to w ogóle słowo?)

Pętla zdarzeń i model współbieżności

Są trzy rzeczy, o których należy pamiętać; Kolejka; pętla zdarzeń i stos

Ogólnie mówiąc, pętla zdarzeń jest jak menedżer projektu, stale nasłuchuje wszelkich funkcji, które chcą uruchamia i komunikuje się między kolejką a stosem.

while (queue.waitForMessage()) {
   queue.processNextMessage();
}

Po otrzymaniu komunikatu do uruchomienia czegoś dodaje go do kolejki. Kolejka jest listą rzeczy, które czekają do wykonania(jak żądanie AJAX). wyobraź sobie to tak:

 1. call foo.com/api/bar using foobarFunc
 2. Go perform an infinite loop
 ... and so on

Gdy któraś z tych komunikatów ma się wykonać, wyskakuje wiadomość z kolejki i tworzy stos, stos jest wszystkim, co JS musi wykonać, aby wykonać instrukcję zawartą w wiadomości. Więc w naszym przykładzie mówi się, że call foobarFunc

function foobarFunc (var) {
  console.log(anotherFunction(var));
}

Wszystko, co foobarFunc musi wykonać (w naszym przypadku anotherFunction), zostanie wepchnięte na stos. wykonywana, a następnie zapomniana - pętla zdarzeń przejdzie do następnej rzeczy w kolejce (lub nasłucha wiadomości)

Kluczowa jest tu kolejność egzekucji. To jest

Kiedy coś ucieknie

Gdy wykonujesz połączenie za pomocą AJAX do zewnętrznej strony lub uruchamiasz dowolny kod asynchroniczny (setTimeout na przykład), Javascript jest zależny od odpowiedzi, zanim będzie mógł kontynuować.

Pytanie brzmi, kiedy otrzyma odpowiedź? Odpowiedź brzmi: nie wiemy - więc pętla zdarzeń czeka na wiadomość z napisem "Hey run me". Jeśli JS po prostu czekał na tę wiadomość synchronicznie aplikacja zamrozi i będzie ssać. Tak więc JS kontynuuje wykonywanie następnego elementu w kolejce, czekając aż wiadomość zostanie dodana z powrotem do kolejki.

Dlatego z asynchronicznym funkcjonalność używamy rzeczy zwanych callbacks. To trochę jak obietnica całkiem dosłownie. Jak w I obiecuję zwrócić coś w pewnym momencie jQuery używa specyficznych wywołań zwrotnych zwanych deffered.done deffered.fail i deffered.always (m.in.). Możesz zobaczyć je wszystkie tutaj

Więc to, co musisz zrobić, to przekazać funkcję, która jest obiecana do wykonania w pewnym momencie z danymi, które są do niej przekazywane.

Ponieważ wywołanie zwrotne nie jest wykonywane od razu, ale później czas ważne jest, aby przekazać odwołanie do funkcji, która nie została wykonana. więc

function foo(bla) {
  console.log(bla)
}

Więc przez większość czasu (ale nie zawsze) zdasz foo nie foo()

Mam nadzieję, że to będzie miało jakiś sens. Kiedy napotkasz takie rzeczy, które wydają się mylące-Gorąco polecam pełne zapoznanie się z dokumentacją, aby przynajmniej ją zrozumieć. To uczyni Cię o wiele lepszym deweloperem.
 7
Author: Matthew Brent,
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-04 15:56:07