Pergunta

A minha pergunta é sobre a elegante paralelização de promessas em Pássaro azul quando você precisa passar o contexto e o argumento para as funções que constroem as promessas.

Para tornar meu problema compreensível e testável, fiz um exemplo sem dependência.

Suponhamos que eu faça cálculos ( 1/(xxx) + 1/(x*x) ) envolvendo um "computador" assíncrono (cujos recursos devem ser liberados).O quadrado e o cubo são calculados de forma assíncrona e independente.

Posso fazer meu cálculo assim:

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

Mas acho que esse uso de all demasiado pesado em comparação com o que é teoricamente possível.Quando você passa uma função como argumento para then, ele é executado.Quando você passa funções para all, eles não são, há um erro.Suspeito que esteja faltando um utilitário ou padrão ...

Existe uma solução para alterá-lo para algo mais simples neste 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);

?

Eu provavelmente poderia hackear Promessa.protótipo.tudo ou definir uma nova função para evitar o aumento do polimorfismo, mas só estou interessado em soluções que não envolvam a modificação de objetos que não possuo.


Anexo:

Veja como é definido o "computador" para meu teste:

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 }
Foi útil?

Solução

Você não precisa usar Promise.all lá.Em vez de fazer:

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

Você pode simplesmente usar:

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

Se tivéssemos funções de seta em node.js, seria tão simples quanto:

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

Promise.all é para ser usado quando você precisar começar uma cadeia de promessas com pelo menos 2 promessas.Por exemplo:

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

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

Outras dicas

Para o caso de uso de gerenciamento de recursos como você mencionou, o bluebird tem Promise.using(). Promise.using() permite que você configure disposer() função para fechar automaticamente o recurso recuperado de forma assíncrona quando você terminar de usar

Promise.join() ajudaria também a combinar os resultados do cube e square métodos assíncronos

Aqui eu reescrevi ligeiramente o seu InitComputer exemplo para ilustrar como isso funciona - agora ele retorna o Computer instância com val adicionado como propriedade, em vez de val, e coloquei endComputer no proto também

observação:você sempre pode usar Promise.method() assim, em vez de retornar um adiado:

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

novo computador init:

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

novo 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top