Sono previsti timeout uguali in ordine in Javascript?

Supponiamo che lo faccia

setTimeout(foo, 0); ... setTimeout(bar, 0); 

Posso essere sicuro che foo inizierà l’esecuzione prima della barra? Cosa succede se invece di 0 utilizzo un timeout di 1, 10 o 100 per barra?

Semplici esperimenti mostrano che nel caso di valori di timeout uguali gli obiettivi di timeout vengono eseguiti nello stesso ordine degli stessi setTimeouts, ma è sicuro affidarsi a questo comportamento?

Non è sicuro fare affidamento su questo comportamento. Ho scritto uno script di test che pianifica un numero di funzioni (e a loro volta programmano anche un numero di funzioni) tutte usando setTimeout (…, 0), e l’ordine in cui sono state chiamate le funzioni non era sempre lo stesso del ordine in cui è stato chiamato setTimeout (almeno in Chrome 11, che usavo per eseguire lo script).

Puoi vedere / eseguire lo script qui: http://jsfiddle.net/se9Jn/ (lo script usa YUI per la compatibilità cross-browser, ma Y.later usa setTimeout internamente).

Si noti che se si esegue lo script e si fissa la console, probabilmente non si vedrà un ordinamento pericoloso. Ma se si avvia lo script, si passa a un’altra scheda, si caricano alcune pagine e si ritorna alla pagina di test, si dovrebbero vedere errori sui callback fuori servizio nella console.

Se hai bisogno di un ordine garantito, ti consiglio di programmare la funzione successiva alla fine della funzione precedente:

 setTimeout(foo, 0); ... function foo() { ... setTimeout(bar, 0); } 

Ho letto il codice sorgente di Firefox e, almeno per quel browser, esiste una garanzia di sequenzialità per i timeout specificati in ordine non crescente. Altrimenti tutte le scommesse sono distriggerste.

Specificamente, se in un determinato contesto di esecuzione si imposta un timeout per n, e quindi uno per m, dove n <= m, i target verranno eseguiti nell'ordine in cui sono stati impostati i timeout.

Ma non fidarti della mia parola. Credo che l’implementazione della finestra sia in nsGlobalWindow.cpp, e il metodo che effettivamente decide dove vanno i timeout nella coda di esecuzione è chiamato InsertTimeoutIntoList . Sembra che il metodo attraversi la coda all’indietro cercando il primo timeout che sia inferiore o uguale al timeout specificato e inserisca il timeout specificato dopo di esso.

Ovviamente quando viene impostato il timeout, l’ora corrente viene aggiunta al valore di timeout. Questo è il motivo per cui i valori di timeout devono essere in ordine di non diminuzione per i target da eseguire in sequenza.

In breve, non contarci. Dai un’occhiata a questo .

Ci sono molte informazioni in questa figura da digerire, ma comprenderle completamente ti darà una migliore comprensione di come funziona l’esecuzione asincrona di JavaScript.

La risposta è: No, l’ordine di esecuzione non è garantito. Di tanto in tanto ho visto ordini non FIFO in chrome 40, specialmente se mi fermo in debugger mentre i timeout sono in sospeso. Ho anche avuto un raro avvistamento in natura – era l’unica spiegazione plausibile di un incidente che ho indagato.

Se hai bisogno di un ordine di esecuzione garantito, invece di setTimeout(callback, 0) puoi farlo:

 var queue = []; function handleQueue() { queue.shift()(); } function queueCallback(callback) { queue.push(callback); setTimeout(handleQueue, 0); } 

Ora, per ordine garantito, ordine bar puoi:

 queueCallback(foo); ... queueCallback(bar); 

Esiste un minimo che il ritardo può effettivamente essere e che dipende molto dal sistema operativo, dal browser, dall’attività del browser e dal carico del computer. Io tendo a non scendere sotto i 20ms perché penso che niente di meno che non faccia alcuna differenza.

Quando si mettono due ritardi uguali, non significa necessariamente che accadrà in questo ordine.

Se vuoi assicurarti che ciò avvenga, tendo a fare qualcosa del genere:

 setTimeout(function() { foo(); setTimeout(bar, 20) }, 20); 

Questo garantirà sempre l’ordine.

Se stai usando Firefox, per assicurarti che il tuo javascript sia effettivamente multithread, potresti voler guardare WebWorker, che è supportato sui nuovi browser, ma non su IE.