Pregunta

Mi pregunta es sobre la elegante paralelización de promesas en Azulejo cuando necesita pasar tanto el contexto como el argumento a las funciones que crean las promesas.

Para que mi problema sea comprensible y comprobable, hice un ejemplo sin dependencia.

Supongamos que hago cálculos ( 1/(xXx) + 1/(x*x) ) que involucra una "computadora" asincrónica (cuyos recursos deben liberarse).El cuadrado y el cubo se calculan de forma asincrónica e independiente.

Puedo hacer mi cálculo así:

InitComputer(2) // returns a promise
.then(invert)
.then(function(arg){
    return Promise.all([
        proto.square(arg),
        proto.cube(arg)
    ]);
}).spread(function(sq, cu){
    this.set(sq + cu);
}).catch(function(err){
    console.log('err:', err);
}).finally(endComputer);

Pero encuentro este uso de all demasiado pesado en comparación con lo que es teóricamente posible.Cuando pasas una función como argumento a then, se ejecuta.Cuando pasas funciones a all, no lo son, hay un error.Sospecho que me falta una utilidad o patrón...

¿Existe una solución para cambiarlo a algo más simple en este estilo?

InitComputer(2)
.then(invert)
.all([
    proto.square,
    proto.cube
]).spread(function(sq, cu){
    this.set(sq + cu);
}).catch(function(err){
    console.log('err:', err);
}).finally(endComputer);

?

Probablemente podría hackear Promesa.prototipo.todo o definir una nueva función para evitar aumentar el polimorfismo, pero solo me interesan soluciones que no impliquen la modificación de objetos que no me pertenecen.


Anexo:

Así es como se define la "computadora" para mi prueba:

var Promise = require("bluebird");

function Computer(){}
function InitComputer(v){
    // initializing a computer is asynchronous and may fail hence the promise
    var c = new Computer(), resolver = Promise.defer();
    setTimeout(function(){
        if (v>1) resolver.resolve(v);
        else resolver.reject(new Error("bad value: "+v));
    },100);
    return resolver.promise.bind(c);
}
var proto = Computer.prototype;
proto.square = function(x){
    // imagine this really uses the computer and is asynchronous
    if (!this instanceof Computer) throw new Error('not a computer');
    return x*x
}
proto.cube = function(x){ return x*x*x }
proto.set = function(v){ this.value = v }

function endComputer(){
    // releases resources here
    console.log('value:', this.value);
}

// this asynchronous function doesn't involve or know the computer
function invert(v){ return 1/v }
¿Fue útil?

Solución

No tienes que usar Promise.all allá.En lugar de hacer:

.then(function(arg){
    return Promise.all([
        proto.square(arg),
        proto.cube(arg)
    ]);
}).spread(...

Simplemente puedes usar:

.then(function(arg){
    return [proto.square(arg), proto.cube(arg)];
}).spread(...

Si tuviéramos funciones de flecha en node.js, sería tan simple como:

.then(arg => [proto.square(arg), proto.cube(arg)]).spread(...

Promise.all debe usarse cuando sea necesario comenzar una cadena de promesas con al menos 2 promesas.Por ejemplo:

var promise1 = somePromise();
var promise2 = somePromise2();

// Start the chain here
Promise.all([promise1, promise2])
.spread(function(value1, value2) {
    // ...
});

Otros consejos

Para el caso de uso de gestión de recursos como el que usted menciona, bluebird tiene Promise.using(). Promise.using() te permite configurar disposer() función para cerrar automáticamente el recurso recuperado de forma asincrónica cuando termine de usar

Promise.join() También ayudaría a combinar los resultados de la cube y square métodos asíncronos

Aquí reescribí ligeramente tu InitComputer ejemplo para ilustrar cómo funciona esto: ahora devuelve el Computer instancia con val agregado como propiedad, en lugar de val, y coloqué endComputer en el proto también

nota:siempre puedes usar Promise.method() así en lugar de devolver un diferido:

var invert = Promise.method(function invert(v){ return 1/v })

nueva computadora de inicio:

function InitComputer(v){
    var c = new Computer(), resolver = Promise.defer();
    setTimeout(function(){
        if (v>1) {
            c.val = v;
            resolver.resolve(c);
        }
        else resolver.reject(new Error("bad value: "+v));
    },100); /** notice resource disposer function added below **/
    return resolver.promise.bind(c).disposer(function(compu){compu.endComputer()});
}

nuevo código:

Promise.using(InitComputer(1.2), function(computer){
    return invert(computer.val)
    .then(function(inverted){
        return Promise.join(computer.square(inverted), computer.cube(inverted), 
            function(sq, cu){
                computer.set(sq + cu)
            }
        )
    })
    .catch(function(err){
        console.log('err:', err);
    });
})
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top