Вопрос

Мой вопрос касается элегантного распараллеливания обещаний в Синяя Птица когда вам нужно передать контекст и аргумент функциям, создающим промисы.

Чтобы сделать мою проблему понятной и проверяемой, я привел пример без каких-либо зависимостей.

Предположим, я выполняю вычисления ( 1/(xИксx) + 1/(x*x) ), включающий асинхронный «компьютер» (ресурсы которого должны быть освобождены).Квадрат и куб вычисляются асинхронно и независимо.

Я могу выполнить свои вычисления следующим образом:

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

Но я считаю, что такое использование all слишком тяжело по сравнению с тем, что теоретически возможно.Когда вы передаете функцию в качестве аргумента then, оно выполнено.Когда вы передаете функции all, это не так, есть ошибка.Я подозреваю, что мне не хватает утилиты или шаблона...

Есть ли решение изменить его на что-то более простое в этом стиле:

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

?

Я, наверное, мог бы взломать Обещание.prototype.all или определить новую функцию, чтобы избежать увеличения полиморфизма, но меня интересуют только решения, не связанные с модификацией объектов, которыми я не владею.


Приложение:

Вот как определяется «компьютер» для моего теста:

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 }
Это было полезно?

Решение

Вам не нужно использовать генеракодицетагкод там.Вместо того, чтобы делать:

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

Вы можете просто использовать:

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

Если бы у нас были функции стрелки в Node.js, это было бы так же просто, как:

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

Promise.all должен использоваться, когда вам нужно Start цепочку обещаний как минимум 2 обещания.Например:

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

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

Другие советы

Для варианта использования ресурсов, о котором вы упоминаете, у bluebird есть Promise.using(). Promise.using() позволяет вам настроить disposer() функция для автоматического закрытия асинхронно полученного ресурса после завершения использования

Promise.join() также поможет объединить результаты cube и square асинхронные методы

Вот я немного переписал ваш InitComputer пример, иллюстрирующий, как это работает - теперь он возвращает Computer экземпляр с добавленным в качестве свойства val вместо val, и я поместил endComputer на прото тоже

примечание:ты всегда можешь использовать Promise.method() вот так вместо возврата отложенного:

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);
    });
})
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top