Динамическая цепочка в Javascript Promises

Как я могу выполнить динамическое цепочку в Javascript Promises, все время я видел только жесткое кодирование вызовов, например., (promise).then(request/functionName).then(request/functionName)

Ответ 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.

Plunker: http://plnkr.co/edit/UP0rhD?p=preview

Ответ 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) { ... });

Надеюсь, это поможет.