Costruzione fluida di promesse parallele
-
21-12-2019 - |
Domanda
La mia domanda riguarda l'elegante parallelizzazione delle promesse in Bluebird quando è necessario passare sia il contesto che discussione alle funzioni che costruiscono le promesse.
Per rendere il mio problema comprensibile e testabile, ho fatto un esempio senza dipendenza.
Supponiamo che io faccia il calcolo (1 / (x x x) + 1 / (x * x)) che coinvolge un "computer" asincrono (le cui risorse devono essere rilasciate). Il quadrato e il cubo sono calcolati in modo asincrono e indipendentemente.
Posso fare il mio calcolo come questo:
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);
.
Ma trovo questo utilizzo di all
troppo pesante rispetto a ciò che è teoricamente possibile. Quando si passa una funzione come argomento a then
, è eseguito. Quando passano le funzioni a all
, non lo sono, c'è un errore. Sospetto che mi manca una utility o un modello ...
C'è una soluzione per cambiarla in qualcosa di più semplice in questo stile:
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);
.
?
Potrei probabilmente hackerare promessi.prototipo.all o definire una nuova funzione per evitare di aumentare il polimorfismo, ma sono solo interessato a soluzioni che non coinvolgono la modifica degli oggetti che non possiedo.
.
Allegato:
Ecco come è definito il "computer" per il mio test:
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 }
. Soluzione
Non è necessario utilizzare Promise.all
lì.Invece di fare:
.then(function(arg){
return Promise.all([
proto.square(arg),
proto.cube(arg)
]);
}).spread(...
.
Puoi semplicemente usare:
.then(function(arg){
return [proto.square(arg), proto.cube(arg)];
}).spread(...
.
Se avessimo funzioni di freccia in node.js, sarebbe così semplice come:
.then(arg => [proto.square(arg), proto.cube(arg)]).spread(...
.
Promise.all
deve essere utilizzato quando è necessario utilizzare START una catena di promesse con almeno 2 promesse.Ad esempio:
var promise1 = somePromise();
var promise2 = somePromise2();
// Start the chain here
Promise.all([promise1, promise2])
.spread(function(value1, value2) {
// ...
});
. Altri suggerimenti
Per la gestione delle risorse utilizzano il caso come se si menziona, Bluebird ha Promise.using()
. Promise.using()
ti consente di impostare disposer()
Funzione per Chiudendo automaticamente la risorsa recuperata in modo asincrono quando hai finito usando
Promise.join()
Aiuterà anche a combinare i risultati dei metodi cube
e square
ASYNC
var invert = Promise.method(function invert(v){ return 1/v })
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()});
}
.
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);
});
})
.