2016-02-24 2 views
0

я делаю что-то вроде этогоКак вернуть значение и обещание от функции в одно и то же время?

var command1; 
var command2; 

var fn = function(param) { 
    var deferred = Q.defer(); 
    var command = spawn(..., [ 
    ... passing different arguments based on param ... 
    ]); 
    ... 
    command.stdout.on('data', function(data) { 
    if (/... if process started successfully .../.test(data)) { 
     deferred.resolve(); 
    } 
    }); 
    ... 
    if (param === 'command1') { 
    command1 = command; 
    } else { 
    command2 = command; 
    } 
    return deferred.promise; 
}; 

Q.all([ 
    fn('command1'), 
    fn('command2') 
]); 

и позже я звоню command1.kill() и command2.kill(). Я думал о прохождении command до resolve, но тогда он никогда не может быть вызван. Я мог бы также пройти command до reject, чтобы я мог позвонить kill там, если что-то пошло не так, но это странно.

Как я могу вернуть command и обещание звонящему по идиоматическому пути? Без условной части в fn. Каковы возможности?

Я также думал о деконструкции функции присваивания ES6, но рассмотрим следующий

... 
    return [command, deferred.promise]; 
} 

[command1, promiseCommand1] = fn('command1'); 
[command2, promiseCommand2] = fn('command2'); 

Q.all([ 
    promise1, 
    promise2.then(Q.all([ 
    promiseCommand1, 
    promiseCommand2  
    ]) 
]); 

, но это не удается (по крайней мере, в моем конкретном случае, когда команды не должны ждать, пока promise2 разрешен), потому что процессы уже на своем пути, когда я проходил promiseCommand1 и promiseCommand2 до Q.all.

Не уверен, что я использовал правильный синтаксис разукрупнения.

Просто совал мне в голову

var command1; 
var command2; 

var fn = function(param, callback) { 
    var deferred = Q.defer(); 
    var command = spawn(..., [...]); 
    ... 
    callback(command); 
    return deferred.promise; 
}; 

Q.all([ 
    fn('command1', function(command) { 
    command1 = command; 
    }), 
    fn('command1', function(command) { 
    command2 = command; 
    }) 
]); 

Любой другой путь?

Update

Со вчерашнего дня я понял, как я мог бы, возможно, использовать назначение деконструкции (до сих пор не уверен в синтаксисе)

Q.all([ 
    promise1, 
    promise2.then(function() { 
    [command1, promiseCommand1] = fn('command1'); 
    [command2, promiseCommand2] = fn('command2');  
    return Q.all([ 
     promiseCommand1, 
     promiseCommand2  
    ]); 
    }) 
]); 

таким образом команды будут выполняться только после того, как promise2 является решена.

Решение

Основываясь на the accepted answer и моем предыдущем обновлении, я пришел с этим

command.promise = deferred.promise; 
    return command; 
}; 

Q.all([ 
    promise1, 
    promise2.then(function() { 
    command1 = fn('command1'); 
    command2 = fn('command2');  
    return Q.all([command1.promise, command2.promise]); 
    }) 
]); 

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

return { 
    command: command, 
    promise: deferred.promise 
}; 

также является возможным решением, но менее кратким.

Q.all([ 
    promise1, 
    promise2.then(function() { 
    var result1 = fn('command1'); 
    var result2 = fn('command2'); 
    command1 = result1.command; 
    command2 = result2.command; 
    return Q.all([result1.promise, result2.promise]); 
    }) 
]); 

Коррекция

В разделе комментариев в принятом ответ я посоветовала позвонить reject в fn, чтобы предотвратить мой код повесить навсегда из-за отложенного обещания.Я решил эту проблему следующим

command.promise = deferred.promise.timeout(...); 
    return command; 
}; 

Использование timeout возвратит такое же обещание, однако, если обещание не будет решена в данном таймаута обещание будет отклонена автоматически.

+0

проблема с 'prom2.then (Q.all ([' является то, что аргумент (ы) для '.then' ** MUST ** является функцией (-ами) - в противном случае они эффективно игнорируются –

+0

@JaromandaX Да, мой пример был выключен. Однако это было образовательным :) –

ответ

1

Вы должны закончить с чем-то полезным, повернув на голову «команду отклонять пройти». Другими словами, отклоните в ответ на команду kill().

Проблема, как вы знаете, заключается в том, что fn() должен вернуть обещание, и в обещании естественно не передается метод соответствующей команды .kill(). Однако javascript позволяет динамически привязывать свойства, включая функции (как методы), к объектам. Поэтому добавление метода .kill() прост.

var fn = function(param) { 
    var deferred = Q.defer(); 
    var command = spawn(..., [ 
     ... passing different arguments based on param ... 
    ]); 
    ... 
    command.stdout.on('data', function(data) { 
     if (/... if process started successfully .../.test(data)) { 
      deferred.resolve(); 
     } 
    }); 
    ... 
    var promise = deferred.promise; 
    // Now monkey-patch the promise with a .kill() method that fronts for command.kill() AND rejects the Deferred. 
    promise.kill = function() { 
     command.kill(); 
     deferred.reject(new Error('killed')); // for a more specific error message, augment 'killed' with something unique derived from `param`. 
    } 
    return promise; 
}; 

var promise1 = fn('command1'); 
var promise2 = fn('command2'); 

Q.all([promise1, promise2]).spread(...).catch(...); 

promise1.kill() или promise2.kill() приведет к «убит» появляется в error.message в обработчике улова.

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

if(...) { 
    promise1.kill(); 
} 
if(...) { 
    promise2.kill(); 
} 

В качестве альтернативы, .kill() методы также будут отделяться чисто без необходимости .bind(), например:

doSomethingAsync(...).then(...).catch(promise1.kill); 
doSomethingElseAsync(...).then(...).catch(promise2.kill); 

Примечание что fn() будет работать для любого количества вызовов без необходимости использования внешних vars command1, command2 и т.д.

+0

Хорошее мышление, однако я не думаю, что смогу это сделать. Я не упоминал об этом в вопросе (подумал, что это не имеет значения, но это действительно так), но я запускаю команды в функции «beforeLaunch» интеграционного теста Protractor, и я вызываю 'kill' в' afterLaunch '. Я возвращаюсь из 'beforeLaunch' с обещанием отложить начало тестов до тех пор, пока мои процессы не будут запущены. В 'afterLaunch' цепочка * не может быть доступна больше (по крайней мере, я не могу получить к ней доступ). –

+0

Просто пришел ко мне. Решение для меня - это почти то же самое, что вы предложили, но вместо того, чтобы исправлять «убить» на «обещании», я заложил «обещание» на объект «ChildProcess». Я обновлю свой ответ, чтобы включить решение. Спасибо за идею! –

+0

Независимо от того, как вы делаете исправление, я все же думаю, что вам нужен механизм, чтобы отклонить Отложенное, когда вызывается .kill(). В противном случае ваш Promise.all ([..., ...]) останется «ожидающим» после того, как была выписана команда kill. –

1

Вы можете вернуть массив, а затем использовать метод promise.spread.

https://github.com/kriskowal/q#combination

.then(function() { 
    return [command, promise]; 
}) 
.spread(function (command, promise) { 
}); 
+0

Если я верну '' [command, deferred.promise]; 'from' fn', то функция не вернет обещание, а массив, второй элемент которого является обещанием. Чтобы использовать 'spread', мне нужно было бы разрешить массив, нет? 'deferred.resolve ([...])' Тогда я мог бы использовать 'spread'. Во всяком случае, я не хочу сожалеть. Можете ли вы уточнить? –