2010-10-15 20 views
9

Questo esempio è JavaScript, poiché è dove utilizzo principalmente i callback. Voglio capire come funzionano a un livello basso.I callback sono sempre asincroni?

Nell'esempio seguente, mi aspetto che tutto avvenga in ordine e che "richiami" si verifichi dopo "passaggio 3" e prima di "passaggio 4". Questo ha senso per me, dato che tutto è fatto in ordine su un singolo thread di esecuzione. Non ci sono inganni. L'unica cosa che è speciale è che hai passato una funzione a un'altra funzione.

function main() { 
    console.log("step 1"); 
    console.log("step 2"); 
    doSomething(myCallBack); 
    console.log("step 4"); 
} 

function doSomething(f) { 
    accessTheDatabase(); // this could take a while 
    console.log("step 3"); 
    f(); // done - now call back 
} 

function myCallBack() { 
    console.log("calling back!"); 
} 

Come si dovrebbe fare doSomething asincrona in modo che "il passo 4" può essere eseguita prima o in parallelo con, "il punto 3"?

Suppongo che se lo doSomething fosse in qualche modo chiamato in modo asincrono, dovrebbe essere su un thread diffferente, no? E se sì, quando finisce e quindi chiama myCallBack, la richiamata avviene sul secondo thread o sul thread principale? Se accade sul thread principale, perché il secondo thread ha bisogno anche di un puntatore alla funzione di callback? Come funziona la chiamata inter-thread?

+0

Presumo che * non * sia in esecuzione in un browser, giusto? Perché nel browser, qualsiasi chiamata AJAX è necessariamente asincrona ... –

+2

@Dean, prima di tutto le richieste XHR possono essere sincrone (a seconda dei parametri della richiesta). In secondo luogo, se leggi la domanda (e guarda il suo codice di esempio), non si tratta di Ajax. – eyelidlessness

+0

@Dean potrebbe essere in esecuzione in un browser o meno. Sono più interessato a capire se la funzione di callback viene eseguita sul thread principale o il secondo thread che ha passato il puntatore della funzione di callback come parametro. –

risposta

11

WebWorkers a parte, il modello di programmazione JavaScript nel browser è puramente a thread singolo. È possibile effettuare la chiamata in qualche modo asincrono utilizzando window.setTimeout:

window.setTimeout(doSomething, 0, myCallBack); 

Questo pone in modo efficace la chiamata doSomething(myCallBack) sulla coda di timer e dopo 0 o più millisecondi trascorsi, finirà per ottenere invocato. Tuttavia, come con tutte le chiamate asincrone in JavaScript, è necessario rinunciare al contesto di esecuzione prima di poter richiamare callback asincroni; vale a dire, la coda del timer non verrà elaborata (e pertanto doSomething(myCallBack) non verrà richiamata) fino a quando non verrà completata la tua funzione main(), supponendo che sia la fine del tuo JavaScript.

Una sfortunata conseguenza di questo approccio basata su setTimeout è che doSomething(myCallBack) non viene invocato in parallelo al fianco console.log("step 4"). D'altra parte, si consideri XMLHttpRequest.send; dopo aver effettuato questa chiamata, il resto del tuo JS può continuare a essere eseguito mentre il browser invia la richiesta HTTP. Lo script deve terminare l'esecuzione prima che il gestore onreadystatechange possa essere eseguito, ma la maggior parte del lavoro di connessione HTTP può avvenire in parallelo mentre JS viene eseguito.

+0

Nel caso 'XMLHttpRequest.send', presumo che ciò avvenga su un altro thread. Quando ha finito e viene eseguito il gestore 'onreadystatechange', su quale thread avviene? –

+1

Questo succede sulla discussione JavaScript principale. Per quanto riguarda l'implementazione, questo può essere qualsiasi thread, ma concettualmente JavaScript (e il DOM, per quanto detto) viene eseguito all'interno di un singolo thread. – ide

+1

Immagino che la parte confusa sia che si chiama "callback". Questo, per me, dice "Ecco una funzione, per favore chiama questo quando hai finito di fare la tua cosa. Sarò fuori a fare le mie cose". In realtà, il browser termina la chiamata AJAX e quindi interrompe il thread principale (tramite quale meccanismo ??) e il thread principale stesso chiama la funzione che ha registrato come callback. Ho ragione? –

0

Avresti bisogno di più thread di esecuzione per fare ciò, che non credo tu possa fare in Javascript (anche se correggimi se sbaglio). Tuttavia, se stai scrivendo un programma C/C++, guarda il pacchetto pthreads (o libdispatch su Mac OS X 10.6).

Modifica: una ricerca su Google per "thread javascript" mostra risultati potenzialmente interessanti.

0

Utilizzo delle richiamate come si fa (nome della dichiarazione di funzione o nome della variabile di espressione della funzione come argomento di un'altra funzione), sono sincroni. Per renderli asincroni, puoi racchiuderli in un setTimeout.

+0

@trinithis, come faccio a sbagliarmi? – eyelidlessness

5

Hmmm .. qualcosa sembra sbagliato (ho fatto v poco JavaScript): si passa myCallBack alla funzione doSomething() ma non si richiama !? Dovresti avere una chiamata a f() all'interno di DoSomething() o passarla a un'altra funzione che la richiamerà una volta che la tua lunga operazione è stata completata. E nessuna callback non è intrinsecamente asincrona - nel tuo caso stai eseguendo tutto sullo stesso thread (anche se accessTheDatabase() è asincrono, nel qual caso ritornerebbe immediatamente!) - quindi andrà ancora Step1, Step2, Step3, Step4. Ho beleive si desidera:

function main() { 
    console.log("step 1"); 
    console.log("step 2"); 
    doSomething(myCallBack); 
    console.log("step 4"); 
} 

function doSomething(f) { 
    accessTheDatabase(f); // assuming this is an asynchronous operation and calls the callback f once done 
} 

function myCallBack() { 
    console.log("step 3"); 
} 

In risposta alla seconda parte della tua domanda: il callback verrebbe eseguito su cui mai infilare si sta chiamando da - per eseguire una richiamata su un altro thread si dovrà join() prima quel thread poi invoke() o begininvoke() il callback - anche se potrebbe esserci già un modo integrato per inviare il callback su una coda di cose da eseguire sul thread su cui volete eseguirlo (spesso il caso con fili dell'interfaccia utente) NOTA: È possibile che accessTheDatabase() esegua il callback sul thread che lo ha chiamato, utilizzando uno dei metodi sopra menzionati ...

+0

Hai ragione! Grazie per la cattura. –

+0

Piacere mio, contrassegnalo come risposta, se è :) – markmnl

+0

risposta aggiornata per la seconda parte della domanda - quale thread viene eseguito su – markmnl

Problemi correlati