Quando JavaScript è sincrono?


202

Ho avuto l'impressione che JavaScript fosse sempre asincrono. Tuttavia, ho imparato che ci sono situazioni in cui non lo è (cioè manipolazioni DOM). C'è un buon riferimento da qualche parte su quando sarà sincrono e quando sarà asincrono? JQuery influisce su tutto ciò?


14
Sempre ad eccezione di Ajax.
defau1t

La risposta accettata è errata e inganna, controllala gentilmente.
Suraj Jain,

2
È stato utile anche guardare youtube.com/watch?v=8aGhZQkoFbQ per capire il ciclo degli eventi e come funzionano lo stack, le API Web e la coda delle attività per quanto riguarda la sincronizzazione e l'asincronizzazione
mtpultz,

1
@ defau1t Non è sbagliato, JavaScript è sempre sincrono, quando una chiamata ajax termina la richiamata finisce in coda, come è un'eccezione alla natura sincrona dello script java.
Suraj Jain,

Risposte:


281

JavaScript è sempre sincrono e a thread singolo. Se stai eseguendo un blocco di codice JavaScript su una pagina, nessun altro JavaScript su quella pagina verrà attualmente eseguito.

JavaScript è asincrono solo nel senso che può effettuare, ad esempio, chiamate Ajax. La chiamata Ajax interromperà l'esecuzione e altri codici saranno in grado di eseguire fino a quando la chiamata non ritorna (correttamente o in altro modo), a quel punto il callback verrà eseguito in modo sincrono. Nessun altro codice verrà eseguito a questo punto. Non interromperà nessun altro codice attualmente in esecuzione.

I timer JavaScript funzionano con questo stesso tipo di richiamata.

Descrivere JavaScript come asincrono è forse fuorviante. È più preciso affermare che JavaScript è sincrono e a thread singolo con vari meccanismi di callback.

jQuery ha un'opzione sulle chiamate Ajax per renderle in modo sincrono (con l' async: falseopzione). I principianti potrebbero essere tentati di utilizzarlo in modo errato perché consente un modello di programmazione più tradizionale a cui si potrebbe essere più abituati. Il motivo per cui è problematico è che questa opzione bloccherà tutto il JavaScript sulla pagina fino al termine, inclusi tutti i gestori e i timer degli eventi.


31
scusa, non ho capito bene questa affermazione "Il codice smetterà di essere eseguito fino a quando la chiamata non verrà restituita (correttamente o per errore)". potresti elaborare. Come può essere vera questa affermazione quando dici anche "Non interromperà nessun altro codice in esecuzione"; Stai parlando di codice di callback solo nella prima istruzione? Per favore, illuminami.
Krishna,

2
Nettuts ha un tutorial che è abbastanza bravo a spiegare le basi di asincrono qui: net.tutsplus.com/tutorials/javascript-ajax/…
RobW

26
@cletus L'istruzione "Il codice smetterà di essere eseguito fino al ritorno della chiamata" necessita di correzione perché l'esecuzione non si interrompe. L'esecuzione del codice può continuare. Altrimenti, significherebbe che la chiamata è sincrona.
HS.

1
Non ho capito anche questa affermazione.
rimorchiato il

12
Questa risposta è incredibilmente fuorviante e confusa. Si prega di consultare invece la risposta di CMS o Faraz Ahmad.
iono il

214

JavaScript è a thread singolo e ha un modello di esecuzione sincrono. Il thread singolo indica che viene eseguito un comando alla volta. Sincrono significa uno alla volta, cioè una riga di codice viene eseguita alla volta nell'ordine in cui appare il codice. Quindi in JavaScript succede una cosa alla volta.

Contesto di esecuzione

Il motore JavaScript interagisce con altri motori nel browser. Nello stack di esecuzione JavaScript c'è un contesto globale in basso e quindi quando invochiamo le funzioni il motore JavaScript crea nuovi contesti di esecuzione per le rispettive funzioni. Quando la funzione chiamata esce, il suo contesto di esecuzione viene estratto dallo stack, quindi viene visualizzato il contesto di esecuzione successivo e così via ...

Per esempio

function abc()
{
   console.log('abc');
}


function xyz()
{
   abc()
   console.log('xyz');
}
var one = 1;
xyz();

Nel codice sopra verrà creato un contesto di esecuzione globale e in questo contesto var one verrà memorizzato e il suo valore sarà 1 ... quando viene chiamato l'invocazione xyz (), verrà creato un nuovo contesto di esecuzione e se avessimo definito una variabile nella funzione xyz tali variabili verrebbero archiviate nel contesto di esecuzione di xyz (). Nella funzione xyz invochiamo abc () e quindi il contesto di esecuzione abc () viene creato e inserito nello stack di esecuzione ... Ora quando abc () termina il suo contesto viene estratto dallo stack, quindi il contesto xyz () viene estratto da lo stack e quindi il contesto globale verranno visualizzati ...

Ora sui callback asincroni; asincrono significa più di uno alla volta.

Proprio come lo stack di esecuzione c'è la coda degli eventi . Quando desideriamo ricevere una notifica relativa ad alcuni eventi nel motore JavaScript, possiamo ascoltarli e quell'evento viene inserito nella coda. Ad esempio un evento di richiesta Ajax o un evento di richiesta HTTP.

Ogni volta che lo stack di esecuzione è vuoto, come mostrato nell'esempio di codice sopra riportato, il motore JavaScript controlla periodicamente la coda degli eventi e verifica se è presente un evento a cui inviare notifiche. Ad esempio nella coda c'erano due eventi, una richiesta Ajax e una richiesta HTTP. Sembra anche che ci sia una funzione che deve essere eseguita su quel trigger di evento ... Quindi il motore JavaScript viene avvisato dell'evento e conosce la rispettiva funzione da eseguire su quell'evento ... Quindi il motore JavaScript invoca il funzione handler, nel caso di esempio, ad esempio AjaxHandler () verrà invocato e come sempre quando viene invocata una funzione, il suo contesto di esecuzione viene inserito nel contesto di esecuzione e ora l'esecuzione della funzione termina e l'evento ajax viene anche rimosso dalla coda degli eventi ... Quando AjaxHandler () termina lo stack di esecuzione è vuoto, quindi il motore guarda di nuovo la coda degli eventi ed esegue la funzione di gestione degli eventi della richiesta HTTP che era successiva nella coda. È importante ricordare che la coda degli eventi viene elaborata solo quando lo stack di esecuzione è vuoto.

Ad esempio, vedere il codice seguente che spiega lo stack di esecuzione e la gestione della coda eventi dal motore Javascript.

function waitfunction() {
    var a = 5000 + new Date().getTime();
    while (new Date() < a){}
    console.log('waitfunction() context will be popped after this line');
}

function clickHandler() {
    console.log('click event handler...');   
}

document.addEventListener('click', clickHandler);


waitfunction(); //a new context for this function is created and placed on the execution stack
console.log('global context will be popped after this line');

E

<html>
    <head>

    </head>
    <body>

        <script src="program.js"></script>
    </body>
</html>

Ora esegui la pagina web e fai clic sulla pagina e vedi l'output sulla console. L'output sarà

waitfunction() context will be popped after this line
global context will be emptied after this line
click event handler...

Il motore JavaScript esegue il codice in modo sincrono, come spiegato nella parte del contesto di esecuzione, il browser inserisce le cose in modo asincrono nella coda degli eventi. Pertanto, le funzioni che richiedono molto tempo per il completamento possono interrompere la gestione degli eventi. Le cose che accadono in un browser come gli eventi sono gestite in questo modo da JavaScript, se c'è un listener che dovrebbe funzionare, il motore lo eseguirà quando lo stack di esecuzione è vuoto. E gli eventi vengono elaborati nell'ordine in cui si verificano, quindi la parte asincrona riguarda ciò che sta accadendo all'esterno del motore, ovvero cosa dovrebbe fare il motore quando si verificano tali eventi esterni.

Quindi JavaScript è sempre sincrono.


16
Questa risposta è molto chiara, dovrebbe ottenere più voti.
ranu,

7
Certamente la migliore spiegazione per il comportamento asincrono di Javascript che ho letto.
Charles Jaimet,

1
Bella spiegazione del contesto di esecuzione e della coda.
Divyanshu Maithani,

1
Naturalmente questo richiede di leggere un po 'dello stack del contesto di esecuzione, e solo l'aggiunta di esso è vuota e l'evento que mi fa finalmente sentire come se avessi capito l'escissione dello script java in modo deterministico. Quel che è peggio è che sento solo una pagina di lettura, ma la trovo quasi da nessuna parte. Allora perché nessuno lo dice e basta? O non lo sanno o cosa? Ma sento che se un tutorial di js avesse questo potrebbe avermi fatto risparmiare un sacco di tempo. >: |
Maresciallo artigianale

2
Spiegazione perfetta!
Julsy,

100

JavaScript è a thread singolo e per tutto il tempo si lavora su una normale esecuzione del flusso di codice sincrono.

Buoni esempi del comportamento asincrono che JavaScript può avere sono eventi (interazione dell'utente, risultati della richiesta Ajax, ecc.) E timer, fondamentalmente azioni che potrebbero verificarsi in qualsiasi momento.

Ti consiglierei di dare un'occhiata al seguente articolo:

Questo articolo ti aiuterà a comprendere la natura a thread singolo di JavaScript e come funzionano i timer internamente e come funziona l'esecuzione asincrona di JavaScript.

async


La risposta accettata inganna possiamo fare qualcosa in quel caso? /
Suraj Jain,

8

A qualcuno che capisce davvero come funziona JS questa domanda potrebbe sembrare off, tuttavia la maggior parte delle persone che usano JS non hanno un livello di conoscenza così profondo (e non ne hanno necessariamente bisogno) e per loro questo è un punto abbastanza confuso, lo farò prova a rispondere da quella prospettiva.

JS è sincrono nel modo in cui viene eseguito il suo codice. ogni linea funziona solo dopo la linea prima che sia stata completata e se quella linea chiama una funzione dopo che è stata completata ect ...

Il principale punto di confusione deriva dal fatto che il tuo browser è in grado di dire a JS di eseguire l'escissione di più codice in qualsiasi momento (simmlar a come puoi estrarre più codice JS su una pagina dalla console). Ad esempio, JS ha funzioni di callback il cui scopo è consentire a JS di ESSERE FORNITO in modo asincrono, in modo che ulteriori parti di JS possano essere eseguite in attesa di una funzione JS che è stata eseguita (ovvero una GETchiamata) per restituire una risposta, JS continuerà a funzionare fino a il browser ha una risposta a quel punto il loop degli eventi (browser) eseguirà il codice JS che chiama la funzione di callback.

Poiché il loop degli eventi (browser) può inserire più JS da eseguire in qualsiasi momento in quel senso JS è asincrono (le cose principali che causeranno l'inserimento di un codice JS da parte del browser sono timeout, callback ed eventi)

Spero che questo sia abbastanza chiaro per essere utile a qualcuno.


4

Definizione

Il termine "asincrono" può essere usato in significati leggermente diversi, dando luogo a risposte apparentemente contrastanti qui, mentre in realtà non lo sono. Wikipedia su Asynchrony ha questa definizione:

L'asincronia, nella programmazione per computer, si riferisce al verificarsi di eventi indipendenti dal flusso del programma principale e ai modi per gestire tali eventi. Questi possono essere eventi "esterni" come l'arrivo di segnali o azioni avviate da un programma che si svolgono in concomitanza con l'esecuzione del programma, senza che il blocco del programma attenda i risultati.

codice non JavaScript può mettere in coda tali eventi "esterni" ad alcune code di eventi JavaScript. Ma questo è quanto va.

Nessuna prevenzione

Non vi è alcuna interruzione esterna dell'esecuzione del codice JavaScript per eseguire qualche altro codice JavaScript nello script. Pezzi di JavaScript vengono eseguiti uno dopo l'altro e l'ordine è determinato dall'ordine degli eventi in ciascuna coda di eventi e dalla priorità di tali code.

Ad esempio, puoi essere assolutamente sicuro che nessun altro JavaScript (nello stesso script) verrà mai eseguito durante l'esecuzione del seguente codice:

let a = [1, 4, 15, 7, 2];
let sum = 0;
for (let i = 0; i < a.length; i++) {
    sum += a[i];
}

In altre parole, non esiste alcuna prelazione in JavaScript. Qualunque cosa possa trovarsi nelle code degli eventi, l'elaborazione di tali eventi dovrà attendere il completamento di tale codice. La specifica EcmaScript dice nella sezione 8.4 Lavori e code lavori :

L'esecuzione di un processo può essere avviata solo quando non è presente un contesto di esecuzione in esecuzione e lo stack del contesto di esecuzione è vuoto.

Esempi di asincronia

Come altri hanno già scritto, ci sono diverse situazioni in cui l'asincronia entra in gioco in JavaScript e comporta sempre una coda di eventi, che può comportare l'esecuzione di JavaScript solo quando non è in esecuzione alcun altro codice JavaScript:

  • setTimeout(): l'agente (ad es. il browser) inserirà un evento in una coda di eventi allo scadere del timeout. Il monitoraggio del tempo e il posizionamento dell'evento nella coda avviene tramite codice non JavaScript, e quindi si può immaginare che ciò avvenga parallelamente alla potenziale esecuzione di un codice JavaScript. Ma il callback fornito setTimeoutpuò essere eseguito solo quando il codice JavaScript attualmente in esecuzione è stato eseguito fino al completamento e viene letta la coda eventi appropriata.

  • fetch(): l'agente utilizzerà le funzioni del sistema operativo per eseguire una richiesta HTTP e monitorare l'eventuale risposta in arrivo. Ancora una volta, questa attività non JavaScript può essere eseguita in parallelo con del codice JavaScript che è ancora in esecuzione. Ma la procedura di risoluzione della promessa, che risolverà la promessa restituita fetch(), può essere eseguita solo quando il JavaScript attualmente in esecuzione è stato completato.

  • requestAnimationFrame(): il motore di rendering del browser (non JavaScript) inserirà un evento nella coda JavaScript quando è pronto per eseguire un'operazione di disegno. Quando viene elaborato l'evento JavaScript, viene eseguita la funzione di callback.

  • queueMicrotask(): inserisce immediatamente un evento nella coda microtask. Il callback verrà eseguito quando lo stack di chiamate è vuoto e quell'evento viene consumato.

Ci sono molti altri esempi, ma tutte queste funzioni sono fornite dall'ambiente host, non dal core EcmaScript. Con il core EcmaScript puoi posizionare un evento in modo sincrono in una coda lavori promessa con Promise.resolve().

Costrutti linguistici

EcmaScript offre diversi costrutti di linguaggio per sostenere il modello di asincronia, come ad esempio yield, async, await. Ma non lasciare errori: nessun codice JavaScript verrà interrotto da un evento esterno. L '"interruzione" che yielde awaitsembra fornire è solo un modo controllato e predefinito di tornare da una chiamata di funzione e ripristinare il suo contesto di esecuzione in un secondo momento, tramite il codice JS (nel caso di yield) o la coda degli eventi (nel caso di await).

Gestione degli eventi DOM

Quando il codice JavaScript accede all'API DOM, in alcuni casi ciò può far sì che l'API DOM attivi una o più notifiche sincrone. E se il tuo codice ha un gestore di eventi che lo ascolta, verrà chiamato.

Ciò può presentarsi come concorrenza preventiva, ma non lo è: una volta restituiti i gestori degli eventi, l'API DOM alla fine tornerà e il codice JavaScript originale continuerà.

In altri casi, l'API DOM invierà un evento nella coda degli eventi appropriata e JavaScript lo raccoglierà una volta svuotato lo stack di chiamate.

Vedi eventi sincroni e asincroni


0

È sincrono in tutti i casi.

Esempio di blocco del thread con Promises:

  const test = () => new Promise((result, reject) => {
    const time = new Date().getTime() + (3 * 1000);

    console.info('Test start...');

    while (new Date().getTime() < time) {
      // Waiting...
    }

    console.info('Test finish...');
  });

  test()
    .then(() => console.info('Then'))
    .finally(() => console.info('Finally'));

  console.info('Finish!');

L'output sarà:

Test start...
Test finish...
Finish!
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.