javascript - mac - ¿Qué debe incluirse en las declaraciones then() en CasperJS? ¿Cómo determinar el orden de ejecución de las funciones de sincronización/asíncrono?
install casperjs mac (1)
Regla general:
todas las funciones de CasperJS que contienen las palabras
then
y
wait
son asíncronas.
Esta declaración tiene muchas excepciones.
¿Qué está haciendo
then()
?
CasperJS está organizado como una serie de pasos que manejan el flujo de control de su script.
then()
maneja los muchos tipos de eventos PhantomJS / SlimerJS que definen el final de un paso.
Cuando se llama a
then()
, la función pasada se coloca en una cola de pasos que es simplemente una matriz de JavaScript.
Si el paso anterior terminó, ya sea porque era una función síncrona simple o porque CasperJS detectó que eventos específicos se activaron, el siguiente paso comenzará a ejecutarse y se repetirá hasta que se ejecuten todos los pasos.
Todas esas funciones de paso están vinculadas al objeto
casper
, por lo que puede referirse a ese objeto usando
this
.
El siguiente script simple muestra dos pasos:
casper.start("http://example.com", function(){
this.echo(this.getTitle());
}).run();
El primer paso es una llamada implícita asíncrona ("escalonada")
open()
detrás de
start()
.
La función
start()
también toma una devolución de llamada opcional, que es el segundo paso en este script.
Durante la ejecución del primer paso se abre la página. Cuando la página se carga por completo, PhantomJS activa el evento onLoadFinished, CasperJS activa sus propios events y continúa con el siguiente paso. El segundo paso es una función simple completamente sincrónica, por lo que no pasa nada lujoso aquí. Cuando se hace esto, CasperJS sale, porque no hay más pasos para ejecutar.
Hay una excepción a esta regla: cuando una función se pasa a la función
run()
, se ejecutará como el último paso en lugar de la salida predeterminada.
Si no llama a
exit()
o
die()
allí, deberá finalizar el proceso.
¿Cómo
then()
detecta que el siguiente paso tiene que esperar?
Tome por ejemplo el siguiente ejemplo:
casper.then(function(){
this.echo(this.getTitle());
this.fill(...)
this.click("#search");
}).then(function(){
this.echo(this.getTitle());
});
Si durante la ejecución de un paso se dispara un evento que denota la carga de una nueva página, CasperJS esperará la carga de la página hasta ejecutar el siguiente paso.
En este caso, se activó un clic que desencadenó un
evento
onNavigationRequested
desde el navegador subyacente.
CasperJS ve esto y suspende la ejecución mediante devoluciones de llamada hasta que se carga la página siguiente.
Otros tipos de tales desencadenantes pueden ser envíos de formularios o incluso cuando el cliente JavaScript hace algo como su propio redireccionamiento con
window.open()
/
window.location
.
Por supuesto, esto se rompe cuando hablamos de aplicaciones de una sola página (con una URL estática).
PhantomJS no puede detectar que, por ejemplo, se está generando una plantilla diferente después de un clic y, por lo tanto, no puede esperar hasta que termine de cargarse (esto puede llevar algún tiempo cuando los datos se cargan desde el servidor).
Si los siguientes pasos dependen de la nueva página, deberá usar, por ejemplo,
waitUntilVisible()
para buscar un selector que sea único para la página que se cargará.
¿Cómo se llama este estilo API?
Algunas personas lo llaman Promesas, debido a la forma en que se pueden encadenar los pasos.
Aparte del nombre (
then()
) y una cadena de acción, ese es el final de las similitudes.
No hay ningún resultado que se pase de devolución de llamada a devolución de llamada a través de la cadena de pasos en CasperJS.
Puede almacenar su resultado en una variable global o agregarlo al objeto
casper
.
Entonces solo hay un manejo limitado de errores.
Cuando se encuentra un error, CasperJS morirá en la configuración predeterminada.
Prefiero llamarlo un patrón Builder, porque la ejecución comienza tan pronto como llamas a
run()
y cada llamada anterior solo está allí para poner los pasos en la cola (ver primera pregunta).
Es por eso que no tiene sentido escribir funciones síncronas fuera de las funciones de paso.
En pocas palabras, se ejecutan sin ningún contexto.
La página ni siquiera comenzó a cargarse.
Por supuesto, esta no es toda la verdad llamándola un patrón generador. Los pasos pueden anidarse, lo que en realidad significa que si programa un paso dentro de otro paso, se colocará en la cola después del paso actual y después de todos los demás pasos que ya se programaron desde el paso actual. (¡Son muchos pasos!)
El siguiente script es una buena ilustración de lo que quiero decir:
casper.on("load.finished", function(){
this.echo("1 -> 3");
});
casper.on("load.started", function(){
this.echo("2 -> 2");
});
casper.start(''http://example.com/'');
casper.echo("3 -> 1");
casper.then(function() {
this.echo("4 -> 4");
this.then(function() {
this.echo("5 -> 6");
this.then(function() {
this.echo("6 -> 8");
});
this.echo("7 -> 7");
});
this.echo("8 -> 5");
});
casper.then(function() {
this.echo("9 -> 9");
});
casper.run();
El primer número muestra la posición del fragmento de código síncrono en el script y el segundo muestra la posición real ejecutada / impresa, porque
echo()
es síncrono.
Puntos importantes:
- El número 3 viene primero
- El número 8 se imprime entre 4 y 5
Para evitar confusiones y problemas difíciles de encontrar, siempre llame a funciones asíncronas después de las funciones síncronas en un solo paso. Si parece imposible, divídalo en varios pasos o considere la recursividad.
¿Cómo funciona
waitFor()
?
waitFor()
es la función más flexible de la familia
wait*
, porque todas las demás funciones usan esta.
waitFor()
programa en su forma más básica (pasando solo una función de verificación y nada más) un paso.
La función de
check
que se le pasa se llama repetidamente hasta que se cumple la condición o se alcanza el tiempo de espera (global).
Cuando se pasa adicionalmente una función de paso
then
y / u
onTimeout
, se llamará en esos casos.
Es importante tener en cuenta que si
waitFor()
agota el tiempo de espera, el script detendrá la ejecución cuando no haya pasado la función de devolución de llamada
onTimeout
, que es esencialmente una función de captura de error:
casper.start().waitFor(function checkCb(){
return false;
}, function thenCb(){
this.echo("inner then");
}, null, 1000).then(function() {
this.echo("outer");
}).run();
¿Cuáles son otras funciones que también son funciones de paso asincrónicas?
A partir de 1.1-beta3, existen las siguientes funciones asincrónicas adicionales que no siguen la regla general:
Módulo Casper:
back()
,
forward()
,
reload()
,
repeat()
,
start()
,
withFrame()
,
withPopup()
Módulo de prueba:
begin()
Si no está seguro, busque en el
código fuente
si una función específica usa
then()
o
wait()
.
¿Los oyentes de eventos son asíncronos?
Los oyentes de eventos se pueden registrar usando
casper.on(listenerName, callback)
y se activarán usando
casper.emit(listenerName, values)
.
En lo que respecta a los aspectos internos de CasperJS, no son astronómicos.
El manejo asincrónico proviene de las funciones donde se encuentran esas llamadas
emit()
.
CasperJS pasa la mayoría de los eventos PhantomJS simplemente, por lo que aquí es donde esos son asíncronos.
¿Puedo salir del flujo de control?
El flujo de control o ejecución es la forma en que CasperJS ejecuta el script. Cuando salimos del flujo de control, necesitamos administrar un segundo flujo (o incluso más). Esto complicará enormemente el desarrollo y la facilidad de mantenimiento del script.
Como ejemplo, desea llamar a una función asincrónica que se define en alguna parte. Supongamos que no hay forma de reescribir la función de tal manera que sea síncrona.
function longRunningFunction(callback) {
...
callback(data);
...
}
var result;
casper.start(url, function(){
longRunningFunction(function(data){
result = data;
});
}).then(function(){
this.open(urlDependsOnFunResult???);
}).then(function(){
// do something with the dynamically opened page
}).run();
Ahora tenemos dos flujos que dependen uno del otro.
Otras formas de dividir directamente el flujo es mediante el uso de las funciones de JavaScript
setTimeout()
y
setInterval()
.
Como CasperJS proporciona
waitFor()
, no hay necesidad de usarlos.
¿Puedo volver al flujo de control CasperJS?
Cuando un flujo de control debe fusionarse nuevamente en el flujo de CasperJS, existe una solución obvia al establecer una variable global y al mismo tiempo esperar que se establezca.
El ejemplo es el mismo que en la pregunta anterior:
var result;
casper.start(url, function(){
longRunningFunction(function(data){
result = data;
});
}).waitFor(function check(){
return result; // `undefined` is evaluated to `false`
}, function then(){
this.open(result.url);
}, null, 20000).then(function(){
// do something with the dynamically opened page
}).run();
¿Qué es asíncrono en el entorno de prueba (módulo de prueba)?
Técnicamente, nada es asíncrono en el módulo de prueba.
Llamar a
test.begin()
simplemente ejecuta la devolución de llamada.
Solo cuando la devolución de llamada en sí usa código asincrónico (lo que significa que se llama a
test.done()
forma asíncrona dentro de una única devolución de llamada
begin()
), los otros casos de prueba
begin()
se pueden agregar a la cola de casos de prueba.
Es por eso que un solo caso de prueba generalmente consiste en una navegación completa con
casper.start()
y
casper.run()
y no al revés:
casper.test.begin("description", function(test){
casper.start("http://example.com").run(function(){
test.assert(this.exists("a"), "At least one link exists");
test.done();
});
});
Es mejor seguir anidando un flujo completo dentro de
begin()
, ya que las llamadas
start()
y
run()
no se mezclarán entre múltiples flujos.
Esto le permite utilizar múltiples casos de prueba completos por archivo.
Notas:
- Cuando hablo de funciones / ejecución síncronas , me refiero a una llamada de bloqueo que en realidad puede devolver lo que computa.
Me está costando mucho determinar qué es asincrónico y qué no mientras se ejecuta CasperJS, qué debe incluirse en las declaraciones then () y qué se evaluará cuando.
Me encontraré con un problema en algún lugar que tenga que ver con una declaración de interrupción por caída, alcance variable o la declaración de evaluación (), y comenzaré a envolver todo mi código en las declaraciones de entonces () ... lo que resulta para no ser el problema
Noto que mi código se ejecuta en dos niveles cuando lo paso, un nivel de evaluación que analiza el código y luego aparecen las declaraciones then (). Además, mis declaraciones impresas aparecen en un orden a veces inexplicable.
Mi pregunta: ¿cómo se ponen en cola estas declaraciones then ()? He leído los documentos y entiendo un poco. Quiero entender las reglas y tener algunas formas simples de determinar qué es sincronizado y qué es asíncrono.
Incluso he leído partes de un libro sobre codificación asíncrona, pero nada parece abordar específicamente la estructura de CasperJS. Cualquier recurso?
Además, ¿cuál es la mejor práctica para dónde colocar sus declaraciones then ()? ¿Deberían estar salpicados generosamente en todo momento, o deberían estar en la función principal de control casper.begin () que llama a los demás?
Gracias amigos, estoy acostumbrado a PHP.