javascript - ejemplo - title css
Entendiendo la sintaxis de una cadena de ejecuciĆ³n diferida (6)
En realidad es muy fácil de entender. Veamos lo que sucede aquí cuando se evalúa la expresión:
Primero se delayedAdd(2, 3)
la función delayedAdd(2, 3)
. Hace algunas cosas y luego regresa. La "magia" tiene que ver con su valor de retorno, que es una function
. Para ser más precisos, es una función que espera al menos un argumento (volveré a eso).
Ahora que evaluamos delayedAdd(2, 3)
a una función, llegamos a la siguiente parte del código, que es el paréntesis de apertura. Apertura y cierre de paréntesis son, por supuesto, llamadas a funciones. Así que vamos a llamar a la función que delayedAdd(2, 3)
acaba de regresar y vamos a pasarle un argumento, que es lo que se define a continuación:
Ese argumento es otra función más (como puede ver en su ejemplo). Esta función también toma un argumento (el resultado del cálculo) y lo devuelve multiplicado por sí mismo.
Esta función que fue devuelta por la primera llamada a delayedAdd(2, 3)
devuelve otra función, a la que llamaremos nuevamente con un argumento que es otra función (la siguiente parte de la cadena).
Entonces, para resumir, construimos una cadena de funciones pasando nuestro código a cualquier función delayedAdd(2, 3)
devuelta. Estas funciones devolverán otras funciones a las que podemos pasar nuestras funciones nuevamente.
Espero que esto haga que la forma en que funciona sea algo clara, si no es así, no dude en preguntar más.
Estoy aprendiendo JavaScript, realmente aprendiendo JavaScript. Vengo de un fondo PHP, por lo que algunos conceptos de JavaScript aún son nuevos para mí, especialmente la programación asíncrona. Es posible que esta pregunta ya haya sido respondida muchas veces antes, pero no he podido encontrar una respuesta. Puede ser porque realmente no sé cómo hacer la pregunta que no sea mostrando un ejemplo. Asi que aqui esta:
Cuando uso el paquete diferido de npm, veo el siguiente ejemplo:
delayedAdd(2, 3)(function (result) {
return result * result
})(function (result) {
console.log(result); // 25
});
Se refieren a esto como un encadenamiento y realmente funciona ya que actualmente estoy usando este código para verificar cuándo se resuelve una promesa o se rechaza. A pesar de que lo llaman encadenamiento, me recuerda a los cierres finales como en Swift.
Realmente no entiendo qué tipo de encadenamiento es este, ya que tenemos una invocación de función e inmediatamente después, una función anónima entre paréntesis.
Así que supongo que tengo dos preguntas.
- ¿Qué patrón es este?
- ¿Como funciona? Esto puede ser una pregunta cargada, pero me gusta saber cómo funciona algo, así que cuando alguien me pregunta sobre esto, puedo darles una explicación detallada.
Aquí está la función delayedAdd:
var delayedAdd = delay(function (a, b) {
return a + b;
}, 100);
que utiliza la siguiente función:
var delay = function (fn, timeout) {
return function () {
var def = deferred(), self = this, args = arguments;
setTimeout(function () {
var value;
try {
value = fn.apply(self, args));
} catch (e) {
def.reject(e);
return;
}
def.resolve(value);
}, timeout);
return def.promise;
};
};
La respuesta de Mhlz es muy clara. Como complemento, aquí compongo un delayedAdd
para que comprenda mejor el proceso.
function delayedAdd(a, b) {
var sum = a + b
return function(f1) {
var result1 = f1(sum)
return function(f2) {
f2(result1)
}
}
}
Donde en su código de ejemplo, la función que pasó como f1
es:
function (result) {
return result * result
}
y f2
es:
function (result) {
console.log(result)
}
Las funciones son ciudadanos de primera clase en JS; eso significa que (entre otros), pueden asumir el papel de parámetros reales y valores de retorno de funciones. Su fragmento de código asigna funciones a funciones.
Las firmas de las funciones en su llamada encadenada podrían verse así.
delayedAdd: number -> fn // returns function type a
a: fn ( number -> number) -> fn // returns function type b
b: fn ( number -> void ) -> void // returns nothing ( guessing, cannot know from your code portion )
Ajustes generales
Por supuesto, JS es un lenguaje de tipificación débil, por lo que las firmas enumeradas se derivan del fragmento de código al adivinar. No hay forma de saber si el código realmente hace lo que se sugiere anteriormente además de inspeccionar las fuentes.
Dado que esto se mostró en el contexto de "encadenamiento", las firmas probablemente se parezcan a esto:
delayedAdd: number x number -> fn (( fn T -> void ) -> ( fn T -> void ))
Lo que significa que delayedAdd
asigna dos números a una función x
, que asigna funciones de firmas arbitrarias a funciones de la misma firma que la misma.
Entonces, ¿quién haría algo como esto? Y por qué ?
Imagina la siguiente implementación de x
:
//
// x
// Collects functions of unspecified (possibly implicit) signatures for later execution.
// Illustrative purpose only, do not use in production code.
//
// Assumes
function x ( fn ) {
var fn_current;
if (this.deferred === undefined) {
this.deferred = [];
}
if (fn === undefined) {
// apply functions
while ( this.deferred.length > 0 ) {
fn_current = this.deferred.shift();
this.accumulator = fn_current(this.accumulator);
}
return this.accumulator;
}
else {
this.deferred.push ( fn );
}
return this;
}
Junto con una función delayedAdd
que en realidad devuelve un objeto del siguiente tipo ...
function delayedAdd ( a1, a2) {
return x ( function () { a1 + a2; } );
}
... registrará efectivamente una cadena de funciones que se ejecutarán en algún momento posterior (por ejemplo, en una devolución de llamada a algún evento).
Notas y recordatorios.
- Las funciones JS son objetos JS
- Las firmas de las funciones registradas pueden ser realmente arbitrarias. Considerar que están unificados solo sirve para mantener esta exposición más simple (bueno ...).
Advertencia
No sé si el código descrito es lo que hace node.js (pero podría ser ... ;-))
Para ser justos, este patrón puede ser encadenamiento o curry (o aplicación parcial). Dependiendo de cómo se implementa. Tenga en cuenta que esta es una respuesta teórica para proporcionar más información sobre el patrón y no su caso de uso específico.
Encadenamiento
No hay nada especial aquí porque solo podemos devolver una función que será llamada nuevamente. Las funciones en javascript son ciudadanos de primera clase.
function delayedAdd(x, y) {
// In here work with x and y
return function(fn) {
// In here work with x, y and fn
return function(fn2) {
//Continue returning functions so long as you want the chain to work
}
}
}
Esto lo hace ilegible en mi opinión. Hay una alternativa mejor.
function delayedAdd(x, y) {
// In here work with x and y
return {
then: function(fn) {
// In here work with x, y and fn
return {
then: function(fn2) {
//Continue returning functions so long as you want the chain to work
}
}
}
}
}
Esto cambia la forma en que sus funciones son llamadas desde
delayedAdd(..)(..)(..); // 25
se transforma en
delayedAdd().then().then()
No solo es más legible cuando está pasando varias funciones de devolución de llamada, sino que permite una distinción del siguiente patrón llamado currying.
Zurra
El término cames según el matemático Haskell Curry . La definicion es esta
En matemáticas e informática, el curry es la técnica de traducir la evaluación de una función que tiene múltiples argumentos (o una tupla de argumentos) para evaluar una secuencia de funciones, cada una con un solo argumento (aplicación parcial). Fue introducido por Moses Schönfinkel y más tarde desarrollado por Haskell Curry.
Básicamente, lo que hace es tomar varios argumentos y fusionarse con los subsecuentes y aplicarlos a la función original pasada en el primer argumento.
Esta es una implementación genérica de esta función tomada de los patrones de Javascript de Stefanv.
{Editar}
Cambié mi versión anterior de la función a una que tiene una aplicación parcial incluida para hacer un mejor ejemplo. En esta versión, debe llamar a la función sin argumentos para obtener el valor devuelto o obtendrá otra función parcialmente aplicada como resultado. Este es un ejemplo muy básico, se puede encontrar uno más completo en esta publicación .
function schonfinkelize(fn) {
var slice = Array.prototype.slice,
stored_args = [],
partial = function () {
if (arguments.length === 0){
return fn.apply(null, stored_args);
} else {
stored_args = stored_args.concat(slice.call(arguments));
return partial;
}
};
return partial;
}
Estos son los resultados de la aplicación de esta función.
function add(a, b, c, d, e) {
return a + b + c + d + e;
}
schonfinkelize(add)(1, 2, 3)(5, 5)(); ==> 16
Tenga en cuenta que add (o, en su caso, delayedAdd) puede implementarse como la función de verificación que da como resultado que el patrón de su ejemplo le proporcione esto.
delayedAdd(..)(..)(..); // 16
Resumen
No se puede llegar a una conclusión sobre el patrón con solo mirar la forma en que se llaman las funciones. Solo porque puedes invocar uno tras otro, lo que no significa es encadenar. Podría ser otro patrón. Eso depende de la implementación de la función.
Si expandimos esta sintaxis de manera lógica alcanzaríamos algo como esto:
var func1 = delayedAdd(2, 3);
var func2 = function (result) {
return result * result
};
var func3 = function (result) {
console.log(result);
};
var res = func1(func2); // variable ''res'' is of type ''function''
res(func3);
Todas las respuestas excelentes aquí, especialmente @mhlz y @Leo, me gustaría referirme a la parte del encadenamiento que mencionó. El ejemplo de Leo muestra la idea de llamar a funciones como foo()()()
pero solo funciona para un número fijo de devoluciones de llamada. Aquí hay un intento de implementar un encadenamiento ilimitado:
delayedAdd = function da(a, b){
// a function was passed: call it on the result
if( typeof a == "function" ){
this.result = a( this.result )
}
else {
// the initial call with two numbers, no additional checks for clarity.
this.result = a + b;
}
// return this very function
return da;
};
Ahora puede encadenar cualquier número de funciones en ()
después de la primera llamada:
// define some functions:
var square = function( number ){ return number * number; }
var add10 = function( number ){ return number + 10; }
var times2 = function( number ){ return number * 2; }
var whatIs = function( number ){ console.log( number ); return number; }
// chain them all!
delayedAdd(2, 3)(square)(whatIs)(add10)(whatIs)(times2)(whatIs);
// logs 23, 35 and 70 in the console.