description cambiar attribute javascript pipe continuation-passing

javascript - cambiar - ¿La continuación del estilo de paso es diferente a las tuberías?



title css (2)

UNIX PIPERS vs ASYNC JavaScript

Hay una gran diferencia fundamental entre la forma en que se comportan las tuberías de Unix frente al código CPS asíncrono al que se vincula.

Principalmente que la tubería bloquea la ejecución hasta que se complete la cadena completa, mientras que su ejemplo de CPS asíncrono regresará justo después de que se realice la primera llamada asíncrona, y solo ejecutará su devolución de llamada cuando se complete. (Cuando se complete el tiempo de espera, en su ejemplo).

Echale un vistazo a éste ejemplo. Usaré Fetch API y Promises para demostrar un comportamiento asíncrono en lugar de setTimeout para hacerlo más realista. Imagine que la primera función f1() es responsable de llamar a algún servicio web y analizar el resultado como un json. Esto se "canaliza" en f2() que procesa el resultado.

Estilo CPS :

function f2(json){ //do some parsing } function f1(param, next) { return fetch(param).then(response => response.json()).then(json => next(json)); } // you call it like this: f1("https://service.url", f2);

Puede escribir algo que parezca sintácticamente como una tubería si mueve la llamada a f2 desde f1, pero eso hará exactamente lo mismo que arriba:

function f1(param) { return fetch(param).then(response => response.json()); } // you call it like this: f1("https://service.url").then(f2);

Pero esto todavía no va a bloquear. No puede hacer esta tarea usando mecanismos de bloqueo en javascript, simplemente no hay ningún mecanismo para bloquear en una Promesa. (Bueno, en este caso podría usar un XMLHttpRequest síncrono, pero ese no es el punto aquí).

CPS vs tubería

La diferencia entre los dos métodos anteriores es que quién tiene el control para decidir si debe llamar al siguiente paso y con exactamente qué parámetros, la persona que llama (ejemplo posterior) o la función llamada (CPS).

Un buen ejemplo donde CPS es muy útil es el middleware s. Piense en un middleware de almacenamiento en caché, por ejemplo, en una tubería de procesamiento. Ejemplo simplificado:

function cachingMiddleware(request, next){ if(someCache.containsKey(request.url)){ return someCache[request.url]; } return next(request); }

El middleware ejecuta alguna lógica, comprueba si la memoria caché sigue siendo válida:

  • Si no lo está, se llama a next , que luego continuará con el proceso de procesamiento.

  • Si es válido, se devuelve el valor almacenado en caché, omitiendo la siguiente ejecución.

He estado aprendiendo sobre el estilo de paso continuo , particularmente la versión asíncrona implementada en javascript, donde una función toma otra función como argumento final y crea una llamada asíncrona, pasando el valor de retorno a esta segunda función.

Sin embargo, no puedo ver cómo el paso de continuación hace algo más que recrear las canalizaciones (como en las líneas de comandos de Unix) o las secuencias:

replace(''somestring'',''somepattern'', filter(str, console.log));

vs

echo ''somestring'' | replace ''somepattern'' | filter | console.log

Excepto que la tubería es mucho, mucho más limpia. Con la canalización, parece obvio que los datos se transmiten y, simultáneamente, la ejecución se pasa al programa receptor. De hecho, con la tubería, espero que el flujo de datos pueda continuar pasando por la tubería, mientras que en CPS espero un proceso en serie.

Es posible, tal vez, que el CPS pueda extenderse a la canalización continua si se pasa un objeto de comunicaciones y un método de actualización junto con los datos, en lugar de un traspaso y devolución completos.

¿Me estoy perdiendo de algo? ¿Es el CPS diferente (mejor?) De alguna manera importante?

Para ser claro, me refiero a la continuación de paso, donde una función pasa la ejecución a otra, no solo devoluciones de llamada simples. CPS parece implicar pasar el valor de retorno de una función a otra función, y luego salir.


Estilo de paso de continuación a nivel de aplicación

En lugar de comparar a nivel de bloque de función / expresión, el estilo de paso de continuación de factorización a nivel de aplicación puede proporcionar una vía para las ventajas de control de flujo a través de su función de "continuación" (también conocida como función de devolución de llamada) . Express.js por ejemplo:

Cada middleware expreso toma una firma de función de CPS bastante similar:

const middleware = (req, res, next) => { /* middleware''s logic */ next(); } const customErrorHandler = (error, req, res, next) => { /* custom error handling logic*/ };

next es la función de devolución de llamada nativa de Express.

Corrección: la función next () no es parte de Node.js o Express API, pero es el tercer argumento que se pasa a la función de middleware. La función next () podría tener cualquier nombre, pero por convención siempre se denomina "next"

req y res son convenciones de nomenclatura para solicitud HTTP y respuesta HTTP respectivamente.

Un controlador de ruta en Express.JS estaría formado por una o más funciones de middleware. Express.js pasará a cada uno de ellos la req , los objetos de res con los cambios realizados por el middleware anterior al siguiente, y una next devolución de llamada idéntica.

app.get(''/get'', middlware1, middlware2, /*...*/ , middlewareN, customErrorHandler)

La next función de devolución de llamada sirve:

  1. Como la continuación de un middleware:

    • La llamada a next() pasa el flujo de ejecución a la siguiente función de middleware. En este caso cumple su función como continuación .
  2. También como interceptor de ruta:

    • La next(''Custom error message'') llamada next(''Custom error message'') pasa por alto todos los middlewares posteriores y pasa el control de ejecución a customErrorHandler para el manejo de errores. ¡Esto hace posible la ''cancelación'' en medio de la ruta!
    • La llamada next(''route'') pasa por alto los middlewares posteriores y pasa el control a la siguiente ruta correspondiente, por ejemplo. / obtener / parte.

Tubo de imitación en JS

Existe una propuesta de TC39 para la pipe , pero hasta que sea aceptada, tendremos que imitar el comportamiento de la tubería manualmente. Las funciones de anidar CPS pueden potencialmente llevar al infierno de devolución de llamada, así que aquí está mi intento por un código más limpio:

Suponiendo que queremos calcular una oración ''El zorro salta sobre la luna'' reemplazando partes de una cadena de inicio (por ejemplo, props )

const props = " The [ANIMAL] [ACTION] over the [OBJECT] "

Cada función para reemplazar diferentes partes de la cadena se secuencia con una matriz

const insertFox = s => s.replace(//[ANIMAL/]/g, ''fox'') const insertJump = s => s.replace(//[ACTION/]/g, ''jumps'') const insertMoon = s => s.replace(//[OBJECT/]/g, ''moon'') const trim = s => s.trim() const modifiers = [insertFox, insertJump, insertMoon, trim]

Podemos lograr un comportamiento de canalización síncrono, sin transmisión, con reduce .

const pipeJS = (chain, callBack) => seed => callBack(chain.reduce((acc, next) => next(acc), seed)) const callback = o => console.log(o) pipeJS(modifiers, callback)(props) //-> ''The fox jumps over the moon''

Y aquí está la versión asíncrona de pipeJS ;

const pipeJSAsync = chain => async seed => await chain.reduce((acc, next) => next(acc), seed) const callbackAsync = o => console.log(o) pipeJSAsync(modifiers)(props).then(callbackAsync) //-> ''The fox jumps over the moon''

¡Espero que esto ayude!