Dlaczego moja zmienna pozostaje niezmieniona po zmodyfikowaniu jej w funkcji? - Odniesienie do kodu asynchronicznego


669

Biorąc pod uwagę następujące przykłady, dlaczego outerScopeVarwe wszystkich przypadkach jest niezdefiniowany?

var outerScopeVar;

var img = document.createElement('img');
img.onload = function() {
    outerScopeVar = this.width;
};
img.src = 'lolcat.png';
alert(outerScopeVar);

var outerScopeVar;
setTimeout(function() {
    outerScopeVar = 'Hello Asynchronous World!';
}, 0);
alert(outerScopeVar);

// Example using some jQuery
var outerScopeVar;
$.post('loldog', function(response) {
    outerScopeVar = response;
});
alert(outerScopeVar);

// Node.js example
var outerScopeVar;
fs.readFile('./catdog.html', function(err, data) {
    outerScopeVar = data;
});
console.log(outerScopeVar);

// with promises
var outerScopeVar;
myPromise.then(function (response) {
    outerScopeVar = response;
});
console.log(outerScopeVar);

// geolocation API
var outerScopeVar;
navigator.geolocation.getCurrentPosition(function (pos) {
    outerScopeVar = pos;
});
console.log(outerScopeVar);

Dlaczego wyświetla się undefinedwe wszystkich tych przykładach? Nie chcę obejść tego problemu, chcę wiedzieć, dlaczego tak się dzieje.


Uwaga: jest to kanoniczne pytanie dotyczące asynchroniczności JavaScript . Popraw to pytanie i dodaj bardziej uproszczone przykłady, z którymi społeczność może się identyfikować.



@Dukeling dzięki, jestem prawie pewien, że skomentowałem ten link, ale najwyraźniej brakuje niektórych komentarzy. Również w odniesieniu do edycji: uważam, że „kanoniczna” i „asynchroniczna” w tytule pomaga w poszukiwaniu tego pytania, aby oznaczyć inne pytanie jako duplikat. I oczywiście pomaga również w znalezieniu tego pytania od Google, gdy szuka się wyjaśnień dotyczących asynchroniczności.
Fabrício Matté

3
Mówiąc nieco więcej, „temat kanonicznej asynchroniczności” jest nieco ciężki w tytule, „odniesienie do kodu asynchronicznego” jest prostsze i bardziej obiektywne. Wierzę również, że większość ludzi szuka „asynchronicznego” zamiast „asynchronicznego”.
Fabrício Matté

1
Niektóre osoby inicjują swoją zmienną przed wywołaniem funkcji. Co powiesz na zmianę tytułu, który w jakiś sposób również to reprezentuje? Jak „Dlaczego moja zmienna pozostaje niezmieniona po zmodyfikowaniu jej w funkcji?” ?
Felix Kling

We wszystkich przykładach kodu wymienionych powyżej „alert (outerScopeVar);” wykonuje się TERAZ, podczas gdy przypisywanie wartości do „outerScopeVar” następuje PÓŹNIEJ (asynchronicznie).
refaktoryzator

Odpowiedzi:


542

Odpowiedź na jedno słowo: asynchroniczność .

Przedmowy

Ten temat został powtórzony co najmniej kilka tysięcy razy tutaj, w przepełnieniu stosu. Dlatego najpierw chciałbym wskazać kilka niezwykle przydatnych zasobów:


Odpowiedź na pytanie

Najpierw prześledźmy wspólne zachowanie. We wszystkich przykładach outerScopeVarjest modyfikowany wewnątrz funkcji . Ta funkcja najwyraźniej nie jest natychmiast wykonywana, jest przypisywana lub przekazywana jako argument. To nazywamy oddzwanianiem .

Teraz pytanie brzmi, kiedy wywoływane jest to oddzwanianie?

To zależy od przypadku. Spróbujmy ponownie prześledzić niektóre typowe zachowania:

  • img.onloadmoże zostać wywołany w przyszłości , gdy (i jeśli) obraz zostanie pomyślnie załadowany.
  • setTimeoutmoże zostać wywołany w przyszłości , po upływie opóźnienia i przekroczeniu limitu czasu clearTimeout. Uwaga: nawet jeśli używasz 0jako opóźnienia, wszystkie przeglądarki mają minimalny limit opóźnienia (określony jako 4ms w specyfikacji HTML5).
  • Wywołanie $.postzwrotne jQuery może zostać wywołane w przyszłości , gdy (i jeśli) żądanie Ajax zostanie pomyślnie zakończone.
  • Plik Node.js fs.readFilemoże zostać wywołany w przyszłości , gdy plik zostanie odczytany pomyślnie lub wystąpi błąd.

We wszystkich przypadkach mamy wywołanie zwrotne, które może zostać uruchomione w przyszłości . To „kiedyś w przyszłości” nazywamy przepływem asynchronicznym .

Wykonanie asynchroniczne jest wypychane z przepływu synchronicznego. Oznacza to, że kod asynchroniczny nigdy się nie uruchomi podczas wykonywania stosu kodu synchronicznego. Takie jest znaczenie JavaScript jako jednowątkowego.

Mówiąc dokładniej, gdy silnik JS jest w stanie bezczynności - nie wykonuje stosu (a) kodu synchronicznego - będzie sondował w poszukiwaniu zdarzeń, które mogły wywołać asynchroniczne wywołania zwrotne (np. Upłynął limit czasu, odebrano odpowiedź sieci) i wykonał je jeden po drugim. Jest to traktowane jako pętla zdarzeń .

Oznacza to, że kod asynchroniczny wyróżniony w ręcznie rysowanych czerwonych kształtach może zostać wykonany tylko po wykonaniu całego pozostałego kodu synchronicznego w odpowiednich blokach kodu:

podświetlony kod asynchroniczny

Krótko mówiąc, funkcje zwrotne są tworzone synchronicznie, ale wykonywane asynchronicznie. Po prostu nie możesz polegać na wykonaniu funkcji asynchronicznej, dopóki nie dowiesz się, że została ona wykonana, i jak to zrobić?

To naprawdę proste. Logikę zależną od wykonania funkcji asynchronicznej należy uruchomić / wywołać z wnętrza tej funkcji asynchronicznej. Na przykład przesunięcie alertsi console.logsi wewnątrz funkcji wywołania zwrotnego wygeneruje oczekiwany wynik, ponieważ wynik jest dostępny w tym momencie.

Implementowanie własnej logiki zwrotnej

Często trzeba robić więcej rzeczy z wynikiem funkcji asynchronicznej lub robić różne rzeczy z wynikiem w zależności od tego, gdzie wywołano funkcję asynchroniczną. Zajmijmy się nieco bardziej złożonym przykładem:

var outerScopeVar;
helloCatAsync();
alert(outerScopeVar);

function helloCatAsync() {
    setTimeout(function() {
        outerScopeVar = 'Nya';
    }, Math.random() * 2000);
}

Uwaga: Używam setTimeoutz losowym opóźnieniem jako rodzajowy funkcji asynchronicznego, ten sam przykład odnosi się do Ajaksu readFile, onloada także wszelkie inne przepływu asynchroniczny.

W tym przykładzie wyraźnie występuje ten sam problem, co w innych przykładach, nie czeka on na wykonanie funkcji asynchronicznej.

Zajmijmy się tym, wdrażając własny system oddzwaniania. Po pierwsze, pozbywamy się tego brzydkiego, outerScopeVarco w tym przypadku jest całkowicie bezużyteczne. Następnie dodajemy parametr, który akceptuje argument funkcji, nasze wywołanie zwrotne. Po zakończeniu operacji asynchronicznej wywołujemy to wywołanie zwrotne, przekazując wynik. Realizacja (proszę przeczytać komentarze w kolejności):

// 1. Call helloCatAsync passing a callback function,
//    which will be called receiving the result from the async operation
helloCatAsync(function(result) {
    // 5. Received the result from the async function,
    //    now do whatever you want with it:
    alert(result);
});

// 2. The "callback" parameter is a reference to the function which
//    was passed as argument from the helloCatAsync call
function helloCatAsync(callback) {
    // 3. Start async operation:
    setTimeout(function() {
        // 4. Finished async operation,
        //    call the callback passing the result as argument
        callback('Nya');
    }, Math.random() * 2000);
}

Fragment kodu powyższego przykładu:

// 1. Call helloCatAsync passing a callback function,
//    which will be called receiving the result from the async operation
console.log("1. function called...")
helloCatAsync(function(result) {
    // 5. Received the result from the async function,
    //    now do whatever you want with it:
    console.log("5. result is: ", result);
});

// 2. The "callback" parameter is a reference to the function which
//    was passed as argument from the helloCatAsync call
function helloCatAsync(callback) {
    console.log("2. callback here is the function passed as argument above...")
    // 3. Start async operation:
    setTimeout(function() {
    console.log("3. start async operation...")
    console.log("4. finished async operation, calling the callback, passing the result...")
        // 4. Finished async operation,
        //    call the callback passing the result as argument
        callback('Nya');
    }, Math.random() * 2000);
}

Najczęściej w rzeczywistych przypadkach użycia DOM API i większość bibliotek już udostępnia funkcję wywołania zwrotnego ( helloCatAsyncimplementacja w tym przykładzie demonstracyjnym). Musisz tylko przekazać funkcję wywołania zwrotnego i zrozumieć, że zostanie ona wykonana poza przepływem synchronicznym, i zrestrukturyzuj kod, aby to uwzględnić.

Zauważysz również, że ze względu na charakter asynchroniczny niemożliwe jest returnprzejście z przepływu asynchronicznego z powrotem do przepływu synchronicznego, w którym zdefiniowano wywołanie zwrotne, ponieważ wywołania asynchroniczne są wykonywane długo po zakończeniu wykonywania kodu synchronicznego.

Zamiast returnwprowadzać wartość z asynchronicznego wywołania zwrotnego, będziesz musiał użyć wzorca wywołania zwrotnego lub ... Obiecuje.

Obietnice

Chociaż istnieją sposoby na powstrzymanie piekła wywołania zwrotnego z waniliowym JS, obietnice rosną w popularności i są obecnie standaryzowane w ES6 (patrz Obietnica - MDN ).

Obietnice (inaczej Futures) zapewniają bardziej liniowy, a zatem przyjemny, odczyt kodu asynchronicznego, ale wyjaśnienie ich całej funkcjonalności nie wchodzi w zakres tego pytania. Zamiast tego pozostawię te doskonałe zasoby zainteresowanym:


Więcej materiałów do czytania na temat asynchroniczności JavaScript

  • Art of Node - Callbacki bardzo dobrze wyjaśnia kod asynchroniczny i callbacki z przykładami waniliowych JS i kodu Node.js.

Uwaga: zaznaczyłem tę odpowiedź jako Wiki społeczności, dlatego każdy z co najmniej 100 reputacjami może ją edytować i ulepszać! Popraw tę odpowiedź lub prześlij zupełnie nową odpowiedź, jeśli chcesz.

Chcę przekształcić to pytanie w kanoniczny temat, aby odpowiedzieć na problemy asynchroniczne niezwiązane z Ajaxem (istnieje jak w tym celu odpowiedzieć na wywołanie AJAX? ), Dlatego ten temat potrzebuje twojej pomocy, aby był tak dobry i pomocny, jak to możliwe !


1
Czy w twoim ostatnim przykładzie jest jakiś konkretny powód, dla którego używasz anonimowych funkcji, czy też działałoby to samo, używając nazwanych funkcji?
JDelage

1
Przykłady kodu są nieco dziwne, gdy deklarujesz funkcję po jej wywołaniu. Działa z powodu podnoszenia, ale czy było to celowe?
Bergi

2
czy to impas? felix kling wskazuje na twoją odpowiedź, a ty wskazujesz na felix odpowiedź
Mahi

1
Musisz zrozumieć, że kod czerwonego kółka jest tylko asynchroniczny, ponieważ jest wykonywany przez NATIVE asynchroniczne funkcje javascript. Jest to cecha twojego silnika javascript - czy to Node.js czy przeglądarki. Jest asynchroniczny, ponieważ jest przekazywany jako „wywołanie zwrotne” do funkcji, która jest zasadniczo czarną skrzynką (zaimplementowana w C itp.). Dla nieszczęsnego programisty są asynchroniczni ... tylko dlatego. Jeśli chcesz napisać własną funkcję asynchroniczną, musisz ją zhakować, wysyłając ją do SetTimeout (myfunc, 0). Powinieneś to zrobić? Kolejna debata ... prawdopodobnie nie.
Sean Anderson

@ Fabricio Szukałem specyfikacji definiującej „> = 4ms clamp”, ale nie mogłem jej znaleźć - znalazłem wzmiankę o podobnym mechanizmie (do blokowania zagnieżdżonych połączeń) na MDN - developer.mozilla.org/en-US/docs / Web / API /… - czy ktoś ma link do prawej części specyfikacji HTML.
Sebi

147

Odpowiedź Fabrício jest natychmiastowa; ale chciałem uzupełnić jego odpowiedź o coś mniej technicznego, co koncentruje się na analogii, która pomoże wyjaśnić pojęcie asynchroniczności .


Analogia ...

Wczoraj praca, którą wykonywałem, wymagała od kolegi pewnych informacji. Zadzwoniłem do niego; oto jak przebiegła rozmowa:

Me : Hi Bob, muszę wiedzieć jak foo 'd bar ' D Ostatnia tygodni. Jim chce raport na ten temat, a ty jesteś jedynym, który zna szczegóły na ten temat.

Bob : Jasne, ale zajmie mi to około 30 minut?

Ja : To świetnie, Bob. Oddzwoń, gdy zdobędziesz informacje!

W tym momencie odłożyłem słuchawkę. Ponieważ potrzebowałem informacji od Boba, aby ukończyć raport, opuściłem raport i zamiast tego poszedłem na kawę, a potem dostałem e-maila. 40 minut później (Bob jest wolny), Bob oddzwonił i dał mi informacje, których potrzebowałem. W tym momencie wznowiłem pracę z moim raportem, ponieważ miałem wszystkie informacje, których potrzebowałem.


Wyobraź sobie, że zamiast tego rozmowa przebiegła w ten sposób;

Me : Hi Bob, muszę wiedzieć jak foo 'd bar ' D Ostatnia tygodni. Jim want ma raport na ten temat, a ty jesteś jedynym, który zna szczegóły na ten temat.

Bob : Jasne, ale zajmie mi to około 30 minut?

Ja : To świetnie, Bob. Poczekam.

I siedziałem tam i czekałem. I czekałem. I czekałem. Przez 40 minut. Nie robiąc nic, tylko czekając. W końcu Bob przekazał mi informacje, rozłączyliśmy się i ukończyłem mój raport. Ale straciłem 40 minut wydajności.


Jest to zachowanie asynchroniczne vs. synchroniczne

Tak właśnie dzieje się we wszystkich przykładach w naszym pytaniu. Ładowanie obrazu, ładowanie pliku z dysku i żądanie strony za pomocą AJAX to powolne operacje (w kontekście współczesnego przetwarzania).

Zamiast czekać na zakończenie tych powolnych operacji, JavaScript pozwala zarejestrować funkcję zwrotną, która zostanie wykonana po zakończeniu powolnej operacji. Jednak w międzyczasie JavaScript będzie nadal wykonywać inny kod. Fakt, że JavaScript wykonuje inny kod w oczekiwaniu na zakończenie powolnej operacji, powoduje, że zachowanie jest asynchroniczne . Gdyby JavaScript czekał na zakończenie operacji przed wykonaniem jakiegokolwiek innego kodu, byłoby to zachowanie synchroniczne .

var outerScopeVar;    
var img = document.createElement('img');

// Here we register the callback function.
img.onload = function() {
    // Code within this function will be executed once the image has loaded.
    outerScopeVar = this.width;
};

// But, while the image is loading, JavaScript continues executing, and
// processes the following lines of JavaScript.
img.src = 'lolcat.png';
alert(outerScopeVar);

W powyższym kodzie prosimy JavaScript do załadowania lolcat.png, co jest powolną operacją. Funkcja wywołania zwrotnego zostanie wykonana po wykonaniu tej powolnej operacji, ale w międzyczasie JavaScript będzie przetwarzać kolejne wiersze kodu; tj alert(outerScopeVar).

Dlatego widzimy ostrzeżenie undefined; ponieważ alert()jest on przetwarzany natychmiast, a nie po załadowaniu obrazu.

Aby naprawić nasz kod, wystarczy przenieść alert(outerScopeVar)kod do funkcji wywołania zwrotnego. W rezultacie nie potrzebujemy już outerScopeVarzmiennej zadeklarowanej jako zmienna globalna.

var img = document.createElement('img');

img.onload = function() {
    var localScopeVar = this.width;
    alert(localScopeVar);
};

img.src = 'lolcat.png';

Będziesz zawsze zobaczyć zwrotna jest określona jako funkcja, ponieważ jest to jedyny sposób w JavaScript * zdefiniować jakiś kod, ale nie wykonać ją później.

Dlatego we wszystkich naszych przykładach function() { /* Do something */ }jest to callback; aby naprawić wszystkie przykłady, wystarczy przenieść tam kod, który wymaga odpowiedzi operacji!

* Technicznie możesz też użyć eval(), ale eval()jest zły do tego celu


Jak sprawić, by mój rozmówca czekał?

Być może masz obecnie kod podobny do tego;

function getWidthOfImage(src) {
    var outerScopeVar;

    var img = document.createElement('img');
    img.onload = function() {
        outerScopeVar = this.width;
    };
    img.src = src;
    return outerScopeVar;
}

var width = getWidthOfImage('lolcat.png');
alert(width);

Wiemy jednak, że return outerScopeVardzieje się to natychmiast; zanim onloadfunkcja wywołania zwrotnego zaktualizuje zmienną. Prowadzi to do getWidthOfImage()powrotu undefinedi undefinedotrzymania ostrzeżenia.

Aby to naprawić, musimy pozwolić funkcji wywołującej getWidthOfImage()na zarejestrowanie wywołania zwrotnego, a następnie przesunąć ostrzeżenie o szerokości, aby znajdowało się w obrębie tego wywołania zwrotnego;

function getWidthOfImage(src, cb) {     
    var img = document.createElement('img');
    img.onload = function() {
        cb(this.width);
    };
    img.src = src;
}

getWidthOfImage('lolcat.png', function (width) {
    alert(width);
});

... tak jak poprzednio, pamiętaj, że byliśmy w stanie usunąć zmienne globalne (w tym przypadku width).


Ale w jaki sposób alarmowanie lub wysyłanie do konsoli jest przydatne, jeśli chcesz użyć wyników w innym obliczeniu lub zapisać je w zmiennej obiektowej?
Ken Ingram

68

Oto bardziej zwięzła odpowiedź dla osób, które szukają szybkiego odniesienia, a także kilka przykładów wykorzystujących obietnice i asynchronizuj / czekaj.

Rozpocznij od naiwnego podejścia (które nie działa) dla funkcji, która wywołuje metodę asynchroniczną (w tym przypadku setTimeout) i zwraca komunikat:

function getMessage() {
  var outerScopeVar;
  setTimeout(function() {
    outerScopeVar = 'Hello asynchronous world!';
  }, 0);
  return outerScopeVar;
}
console.log(getMessage());

undefinedzostaje zalogowany w tym przypadku, ponieważ getMessagewraca przed setTimeoutwywołaniem oddzwaniania i aktualizuje się outerScopeVar.

Dwoma głównymi sposobami rozwiązania tego problemu są połączenia zwrotne i obietnice :

Callbacki

Zmiana polega na getMessagezaakceptowaniu callbackparametru, który zostanie wywołany w celu dostarczenia wyników z powrotem do kodu wywołującego, gdy będzie dostępny.

function getMessage(callback) {
  setTimeout(function() {
    callback('Hello asynchronous world!');
  }, 0);
}
getMessage(function(message) {
  console.log(message);
});

Obietnice

Obietnice stanowią alternatywę, która jest bardziej elastyczna niż wywołania zwrotne, ponieważ można je naturalnie łączyć w celu koordynowania wielu operacji asynchronicznych. Promises / A + standardowy realizacja jest warunkiem natywnie w node.js (0.12+) oraz wielu obecnych przeglądarek, ale jest również realizowany w bibliotekach, jak Bluebird i Q .

function getMessage() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('Hello asynchronous world!');
    }, 0);
  });
}

getMessage().then(function(message) {
  console.log(message);  
});

jQuery Deferreds

jQuery zapewnia funkcjonalność podobną do obietnic z Odroczonymi.

function getMessage() {
  var deferred = $.Deferred();
  setTimeout(function() {
    deferred.resolve('Hello asynchronous world!');
  }, 0);
  return deferred.promise();
}

getMessage().done(function(message) {
  console.log(message);  
});

async / czekaj

Jeśli twoje środowisko JavaScript obsługuje asynci await(jak Node.js 7.6+), możesz używać obietnic synchronicznie w ramach asyncfunkcji:

function getMessage () {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve('Hello asynchronous world!');
        }, 0);
    });
}

async function main() {
    let message = await getMessage();
    console.log(message);
}

main();

Twoja próbka z Obietnic jest w zasadzie tym, czego szukałem przez ostatnie kilka godzin. Twój przykład jest piękny i jednocześnie wyjaśnia obietnice. Dlaczego nie jest to nigdzie indziej, jest oszałamiające.
Vincent P

Wszystko w porządku, ale co zrobić, jeśli trzeba wywołać getMessage () z parametrami? Jak napiszesz powyższe w tym scenariuszu?
Chiwda

2
@Chiwda Wystarczy umieścić parametr wywołania zwrotnego ostatnia: function getMessage(param1, param2, callback) {...}.
JohnnyHK

Próbuję twojej async/awaitpróbki, ale mam problemy. Zamiast tworzenia a new Promise, wykonuję .Get()połączenie i dlatego nie mam dostępu do żadnej resolve()metody. W ten sposób getMessage()zwracam obietnicę, a nie wynik. Czy mógłbyś trochę edytować swoją odpowiedź, aby pokazać działającą składnię?
InteXX

@InteXX Nie jestem pewien, co masz na myśli mówiąc o nawiązaniu .Get()połączenia. Prawdopodobnie najlepiej opublikować nowe pytanie.
JohnnyHK

52

Mówiąc oczywiste, puchar reprezentuje outerScopeVar.

Funkcje asynchroniczne są jak ...

asynchroniczne wezwanie do kawy


13
Podczas gdy próba synchronicznego działania funkcji asynchronicznej oznaczałaby próbę wypicia kawy po 1 sekundzie i nalania jej na kolana po 1 minucie.
Teepeemm,

Gdyby stwierdzał to, co oczywiste, nie sądzę, aby pytanie zostało zadane, nie?
broccoli2000

2
@ broccoli2000 Nie miałem przez to na myśli, że pytanie było oczywiste, ale oczywiste, co puchar reprezentuje na rysunku :)
Johannes Fahrenkrug

13

Pozostałe odpowiedzi są doskonałe, a ja po prostu chcę udzielić bezpośredniej odpowiedzi na to pytanie. Tylko ograniczenie do asynchronicznych wywołań jQuery

Wszystkie wywołania ajax (w tym $.getlub $.postlub $.ajax) są asynchroniczne.

Biorąc pod uwagę twój przykład

var outerScopeVar;  //line 1
$.post('loldog', function(response) {  //line 2
    outerScopeVar = response;
});
alert(outerScopeVar);  //line 3

Wykonanie kodu rozpoczyna się od wiersza 1, deklaruje zmienną oraz wyzwala i asynchroniczne wywołanie w wierszu 2 (tj. Żądanie postu) i kontynuuje wykonywanie od linii 3, nie czekając na zakończenie żądania postu.

Powiedzmy, że wykonanie żądania pocztowego zajmuje 10 sekund, wartość outerScopeVarzostanie ustawiona dopiero po tych 10 sekundach.

Spróbować,

var outerScopeVar; //line 1
$.post('loldog', function(response) {  //line 2, takes 10 seconds to complete
    outerScopeVar = response;
});
alert("Lets wait for some time here! Waiting is fun");  //line 3
alert(outerScopeVar);  //line 4

Teraz, kiedy to wykonasz, dostaniesz alert w linii 3. Teraz poczekaj trochę czasu, aż będziesz pewien, że żądanie post zwróciło pewną wartość. Następnie po kliknięciu OK w polu alertu następny alert wydrukuje oczekiwaną wartość, ponieważ na nią czekałeś.

W scenariuszu z życia kod staje się

var outerScopeVar;
$.post('loldog', function(response) {
    outerScopeVar = response;
    alert(outerScopeVar);
});

Cały kod, który zależy od wywołań asynchronicznych, jest przenoszony do bloku asynchronicznego lub przez oczekiwanie na wywołania asynchroniczne.


or by waiting on the asynchronous callsJak to zrobić?
InteXX

@InteXX Za pomocą metody wywołania zwrotnego
Teja

Czy masz szybki przykład składni?
InteXX

10

We wszystkich tych scenariuszach outerScopeVarmodyfikuje się lub przypisuje wartość asynchronicznie lub dzieje się w późniejszym czasie (oczekiwanie lub oczekiwanie na wystąpienie jakiegoś zdarzenia), na które bieżące wykonanie nie będzie czekać . Więc we wszystkich tych przypadkach bieżący przepływ wykonania powodujeouterScopeVar = undefined

Omówmy każdy przykład (zaznaczyłem część, która jest wywoływana asynchronicznie lub opóźniona w przypadku wystąpienia niektórych zdarzeń):

1.

wprowadź opis zdjęcia tutaj

Tutaj rejestrujemy listę zdarzeń, która zostanie wykonana po tym konkretnym zdarzeniu. Tutaj ładowanie obrazu. Następnie bieżące wykonanie jest kontynuowane z kolejnymi wierszami, img.src = 'lolcat.png';a alert(outerScopeVar);tymczasem zdarzenie może się nie pojawić. tzn. funkcja img.onloadczeka asynchronicznie na załadowanie wskazanego obrazu. Stanie się tak na każdym z poniższych przykładów - wydarzenie może się różnić.

2)

2)

Tutaj zdarzenie timeout odgrywa rolę, która wywoła procedurę obsługi po określonym czasie. Tutaj jest 0, ale nadal rejestruje zdarzenie asynchroniczne, zostanie dodane do ostatniej pozycji Event Queuewykonania, co gwarantuje gwarantowane opóźnienie.

3)

wprowadź opis zdjęcia tutaj Tym razem oddzwanianie ajax.

4

wprowadź opis zdjęcia tutaj

Węzeł można uznać za króla kodowania asynchronicznego. Tutaj zaznaczona funkcja jest rejestrowana jako moduł obsługi wywołania zwrotnego, który zostanie wykonany po odczytaniu określonego pliku.

5

wprowadź opis zdjęcia tutaj

Oczywista obietnica (coś zostanie zrobione w przyszłości) jest asynchroniczna. zobacz Jakie są różnice między odroczeniem, obietnicą i przyszłością w JavaScript?

https://www.quora.com/Whats-the-difference-between-a-promise-and-a-callback-in-Javascript

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.