2010-10-15 25 views
9

Este ejemplo es JavaScript, ya que es donde estoy usando devoluciones de llamada en su mayoría. Quiero entender cómo funcionan en un nivel bajo.¿Las devoluciones de llamada son siempre asincrónicas?

En el ejemplo siguiente, esperaría que todo sucediera en orden y que "volviera a llamar" después del "paso 3" y antes del "paso 4". Esto tiene sentido para mí, ya que todo se hace en orden en un único hilo de ejecución. No hay engaño. Lo único que es especial es que pasaste una función a otra función.

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!"); 
} 

¿Cómo hacer doSomething asíncrona para que "paso 4" puede ejecutarse antes, o en paralelo con el "paso 3"?

Supongo que si de alguna manera se llamara doSomething de forma asíncrona, tendría que estar en un hilo diferente, ¿no? Y si es así, cuando termina y luego llama a myCallBack, ¿la devolución de llamada ocurre en el segundo hilo o en el hilo principal? Si sucede en el hilo principal, ¿por qué el segundo hilo incluso necesita un puntero a la función de devolución de llamada? ¿Cómo funciona la llamada entre hilos?

+0

Supongo que esto * no * se está ejecutando en un navegador, ¿verdad? Porque en el navegador, cualquier llamada AJAX es necesariamente asíncrona ... –

+2

@Dean, en primer lugar, las solicitudes XHR pueden ser sincrónicas (dependiendo de los parámetros de solicitud). En segundo lugar, si lees la pregunta (y miras su código de ejemplo), no se trata de Ajax. – eyelidlessness

+0

@Dean podría estar ejecutándose en un navegador o no. Estoy más interesado en averiguar si la función de devolución de llamada se ejecuta en el subproceso principal o en el segundo subproceso que pasó el puntero de función de devolución de llamada como parámetro. –

Respuesta

11

WebWorkers aparte, el modelo de programación de JavaScript en el navegador es puramente de un solo subproceso. Puede hacer su llamada asincrónica mediante el uso de un poco window.setTimeout:

window.setTimeout(doSomething, 0, myCallBack); 

Esto coloca de manera efectiva la llamada doSomething(myCallBack) en la cola del temporizador, y después de 0 o más milisegundos transcurren, con el tiempo obtendrá invocado. Sin embargo, al igual que con todas las llamadas asíncronas en JavaScript, debe renunciar al contexto de ejecución antes de que se puedan invocar las devoluciones de llamada asíncronas; es decir, la cola del temporizador no se procesará (y por lo tanto no se invocará doSomething(myCallBack)) hasta que finalice su función main(), suponiendo que ese sea el final de su JavaScript.

Una consecuencia desafortunada de este enfoque basado en setTimeout es que doSomething(myCallBack) no se invoca en paralelo junto con console.log("step 4"). Por otro lado, considere XMLHttpRequest.send; después de realizar esta llamada, el resto de su JS puede continuar ejecutándose mientras el navegador emite la solicitud HTTP. El script debe finalizar la ejecución antes de que se ejecute el controlador onreadystatechange, pero la mayor parte del trabajo de conexión HTTP puede realizarse en paralelo mientras se ejecuta JS.

+0

En el caso 'XMLHttpRequest.send', supongo que eso sucede en otro hilo. Cuando termina y se ejecuta el controlador 'onreadystatechange', ¿qué hilo ocurre? –

+1

Esto ocurre en el hilo principal de JavaScript. En lo que respecta a la implementación, esto puede ser cualquier hilo, pero conceptualmente JavaScript (y el DOM, para el caso) se ejecuta dentro de un único hilo. – ide

+1

Supongo que la parte confusa es que se llama "devolución de llamada". Eso, para mí, dice "aquí hay una función, por favor llámala cuando termines de hacer lo tuyo. Voy a dejar de hacer lo mío". Cuando realmente, el navegador termina la llamada AJAX y luego interrumpe el hilo principal (¿a través de qué mecanismo?) Y el hilo principal en sí llama a la función que registró como la devolución de llamada. ¿Estoy en lo cierto? –

0

Necesitarás múltiples hilos de ejecución para hacer esto, lo cual no creo que puedas hacer en Javascript (aunque corrígeme si estoy equivocado). Sin embargo, si estaba escribiendo un programa C/C++, mire el paquete pthreads (o libdispatch en Mac OS X 10.6).

Editar: una búsqueda en Google de "hilos de javascript" muestra algunos resultados posiblemente interesantes.

0

Usando devoluciones de llamada tal como es (nombre de declaración de función o nombre de variable de expresión de función como argumento para otra función), son sincrónico. Para hacerlos asíncronos, puede envolverlos en un setTimeout.

+0

@trinithis, ¿cómo me equivoco? – eyelidlessness

5

Hmmm ... algo parece estar mal (he hecho v pequeño JavaScript): pasas myCallBack a la función doSomething() pero no la vuelves a llamar? Debería tener una llamada a f() dentro de doSomething() o pasarlo a otra función que lo devolverá una vez que se complete su operación larga. Y ninguna devolución de llamada no es inherentemente asíncrona; en su caso, usted está ejecutando todo en el mismo subproceso (incluso si accessTheDatabase() es asíncrono, en cuyo caso volvería inmediatamente!) - por lo que seguirá yendo Paso1, Paso2, Paso3, Paso4. Que creo que quiere:

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"); 
} 

En respuesta a la segunda parte de su pregunta: la devolución de llamada se ejecuta en la que jamás hilo que está llamando desde - para ejecutar una devolución de llamada en el otro hilo que tendría que join() ese subproceso primero y luego invoque() o begininvoke() la devolución de llamada, aunque es posible que ya haya algunos integrados para enviar su devolución de llamada a una cola de cosas que se ejecutarán en el subproceso que desea ejecutar (a menudo caso con hilos UI) NOTA: Puede ser que accessTheDatabase() ejecute la devolución de llamada en el hilo que lo llamó, utilizando uno de los métodos antes mencionados ...

+0

¡Tiene razón! Gracias por la captura. –

+0

Mi placer, márcalo como la respuesta si es :) – markmnl

+0

respuesta actualizada para la segunda parte de tu pregunta, en qué hilo se ejecuta – markmnl

Cuestiones relacionadas