Как я могу выполнить динамическое цепочку в Javascript Promises, все время я видел только жесткое кодирование вызовов, например., (promise).then(request/functionName).then(request/functionName)
Динамическая цепочка в Javascript Promises
Ответ 1
Один из вариантов - использовать свойства объектов и возможность их запуска через строки.
Я написал небольшой образец здесь и разместил его ниже.
Идея состоит в том, что у вас есть набор функций, которые вы хотите запустить, заданные в каком-либо пространстве имен или объекте, как это было в "myNamespace":
myNamespace = {
"A": function() {return "A Function";},
"B": function() {return "B Function";},
"C": function() {return "C Function";}
}
Тогда ваше основное обещание запустится и каким-то образом (через входы, ajax, подсказки и т.д.) вы получите строковое значение функции, которую вы хотите запустить, что неизвестно до выполнения:
В моем главном обещании используется запрос на получение письма от пользователя:
var answer = prompt('Starting. Please pick a letter: A,B,C');
if(myNamespace[answer] === undefined)
{
alert("Invalid choice!");
reject("Invalid choice of: " + answer);
}
else
{
resolve(answer);
}
В следующем "затем" я использую это значение (переданное через функцию разрешения) для вызова функции:
.then(function(response) {
funcToRun = myNamespace[response]();})
Наконец, я выводил на html результат моего динамического вызова функции, и я использую некоторую рекурсивную забаву, чтобы сделать ее более интерактивной и продемонстрировать, что она динамическая:
.then(function(){
document.getElementById('result').innerHTML = funcToRun;})
.then(function(){
if(prompt("Run Again? (YES/NO)")==="YES")
{
doWork();
}
});
myNamespace = {
"A": function() {return "A Function";},
"B": function() {return "B Function";},
"C": function() {return "C Function";}
}
function doWork()
{
var funcToRun;
new Promise(function(resolve,reject) {
var answer = prompt('Starting. Please pick a letter: A,B,C');
if(myNamespace[answer] === undefined)
{
alert("Invalid choice!");
reject("Invalid choice of: " + answer);
}
else
{
resolve(answer);
}
})
.then(function(response) {
funcToRun = myNamespace[response]();})
.then(function(){
document.getElementById('result').innerHTML = funcToRun;})
.then(function(){
if(prompt("Run Again? (YES/NO)")==="YES")
{
doWork();
}
});
}
doWork();
<div id="result"></div>
Ответ 2
Учитывая, что функции массива возвращают все promises, вы можете использовать reduce()
для их последовательного запуска:
var myAsyncFuncs = [
function (val) {return Promise.resolve(val + 1);},
function (val) {return Promise.resolve(val + 2);},
function (val) {return Promise.resolve(val + 3);},
];
myAsyncFuncs.reduce(function (prev, curr) {
return prev.then(curr);
}, Promise.resolve(1))
.then(function (result) {
console.log('RESULT is ' + result); // prints "RESULT is 7"
});
В приведенном выше примере используется ES6 Promises, но все библиотеки обещаний имеют схожие функции.
Кроме того, создание массива функций возврата обещаний обычно является хорошим кандидатом на использование map()
. Например:
myNewOrmModels.map(function (model) {
return model.save.bind(model);
}).reduce(function (prev, curr) {
return prev.then(curr);
}, Promise.resolve())
.then(function (result) {
console.log('DONE saving');
});
Ответ 3
Так как promises развернуть, просто продолжайте добавлять инструкции then
, и он будет продолжать соединяться вместе
function asyncSeries(fns) {
return fns.reduce(function(p, fn) {
return p.then(fn);
}, Promise.resolve());
}
Рекурсивно это довольно классный способ сделать это:)
function countTo(n, sleepTime) {
return _count(1);
function _count(current) {
if (current > n) {
return Promise.resolve();
}
return new Promise(function(resolve, reject) {
console.info(current);
setTimeout(function() {
resolve(_count(current + 1));
}, sleepTime);
});
}
}
Ответ 4
Это решение основано на использовании promises введенного в EcmaScript 6 (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise), поэтому перед его использованием см. браузер таблицы `s support https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise#Browser_compatibility
код
var f1 = function(){
for (var i = 0; i < 800000000; i++) {}
console.log('Function1 is done');
}
var f2 = function(){
for (var i = 0; i < 800000000; i++) {}
console.log('Function2 is done');
}
var f3 = function(){
for (var i = 0; i < 800000000; i++) {}
console.log('Function3 is done');
}
var f4 = function(){
for (var i = 0; i < 800000000; i++) {}
console.log('Function4 is done');
}
callbacks = function(){
// copy passed arguments
var callbacks = arguments;
// create array functions
var callbacks = Object.keys(callbacks).map(function(el){ return callbacks[el] });
var now = Date.now();
callbacks.reduce(function(previousPromise, currentFunc){
return previousPromise.then(
function(){
currentFunc();
var seconds = (Date.now() - now) / 1000;
console.log('Gone', seconds, 'seconds');
}
)
}, Promise.resolve());
}
callbacks(f1, f2, f3, f4);
Результат в консоли Chrome (значения секунд будут разными):
Function1 is done
Gone 1.147 seconds
Function2 is done
Gone 2.249 seconds
Function3 is done
Gone 3.35 seconds
Function4 is done
Gone 4.47 seconds
Примечания:
- Это не работает, если функция содержит таймер (для этой проблемы Я также пытаюсь использовать jQuery `$ Callbacks, $.Ajax и $. Если это не поможет. Единственное решение, которое я нашел, использование resolve() в обратном вызове таймер, но это неприемлемо, если вы выполнили функции.).
- Условия тестирования
$ google-chrome --version
Google Chrome 53.0.2785.116
Ответ 5
Это путь ES7.
Скажем, у вас есть несколько promises, определенных в массиве.
var funcs = [
_ => new Promise(res => setTimeout(_ => res("1"), 1000)),
_ => new Promise(res => setTimeout(_ => res("2"), 1000))
}
И вы хотите так называть.
chainPromises(funcs).then(result => console.log(result));
Вы можете использовать async
и await
для этой цели.
async function chainPromises(promises) {
for (let promise of promises) { // must be for (.. of ..)
await promise();
}
}
Это будет выполнять указанные функции последовательно (один за другим), а не параллельно. Параметр promises
представляет собой массив функций, возвращающих Promise
.
Ответ 6
У меня просто была проблема с моим провайдером api, что выполнение Promise.all() закончилось бы в concurrency проблемах с db..
Сделка с моей ситуацией заключается в том, что мне нужно получить каждый результат обещания, чтобы показать предупреждение "все нормально" или "некоторая полученная ошибка".
И я не знаю, почему.. этот маленький кусочек кода, который использует сокращение, когда разрешено promises, я не мог заставить свою область работать (слишком поздно, чтобы исследовать сейчас)
$scope.processArray = function(array) {
var results = [];
return array.reduce(function(p, i) {
return p.then(function() {
return i.then(function(data) {
results.push(data);
return results;
})
});
}, Promise.resolve());
}
Итак, благодаря этому сообщению http://hellote.com/dynamic-promise-chains/ Я пришел с этим маленьким ублюдком. Он не отполирован, но все работает нормально.
$scope.recurse = function(promises, promisesLength, results) {
if (promisesLength === 1) {
return promises[0].then(function(data){
results.push(data);
return results;
});
}
return promises[promisesLength-1].then(function(data) {
results.push(data);
return $scope.recurse(promises, promisesLength - 1, results);
});
}
Затем я вызываю функцию следующим образом:
var recurseFunction = $scope.recurse(promises, promises.length, results);
recurseFunction.then(function (response) { ... });
Надеюсь, это поможет.