2015-06-15 7 views
13

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

+0

Dynamic - где они исходят? В чем проблема, которую вы пытаетесь решить? Конечно, вы можете передавать любые обратные вызовы, которые вам нравятся, но выбранные, а не только жестко выраженные выражения функций. – Bergi

+0

Поблагодарите Bergi за ответ, они исходят от действий пользователя/событий, таких как щелчки на кнопках и т. Д., –

+0

Вы имеете в виду «var prom = ...; button.onclick = function (e) {prom.then (doSomething); }; '? Конечно, вы можете это сделать, да. Ты это пробовал? – Bergi

ответ

5

Одним из вариантов является использование свойств объектов и возможность их вызова через строки.

Я написал небольшой образец Here и разместил его ниже.

Идея заключается в том, что у вас есть набор функций, которые вы хотите запустить набор в некотором пространстве имен или объект, как я сделал в «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); 
     } 

В следующем «затем» Я использую это значение (передается через функцию Resolve) для вызова функции:

.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>

+0

Спасибо Джейсону ... Я постараюсь выполнить то же самое и сообщить вам –

+0

Привет, Джейсон. ... Быстрый вопрос .... Я получаю много запросов (fns, которые вызывают) от действий пользователя. Как проверить текущий статус обещания, а затем цепочку, если он все еще находится в ожидании или просто запускается напрямую, если статус разрешен –

+0

@RamuAjay так, есть значение состояния Promise, но оно личное. Я предполагаю использовать '.done (function() {isPromiseDone = true;})' или какую-то внешнюю переменную, чтобы удерживать текущее состояние обещания. Что касается добавления дополнительных «thens», вы могли бы провести массив функций, а в функции '.done()' вы могли бы проверить этот массив и добавить больше «thens» к обещанию ... это только предположение. – JasonWilczak

4

Поскольку обещания UnWrap, просто продолжать добавлять 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); 
    }); 
    } 
} 
+0

Спасибо, Трэвис ...... выполнит все предложения –

+0

Привет Трэвис .... Быстрый вопрос .... Я получаю много запросов (fns, которые будут называться) от действий пользователя, Как я могу проверить текущее обещание статус, а затем привязать его к нему, если он все еще ожидает или просто запустит следующую функцию напрямую, если состояние обещания будет разрешено (разрешено или отклонено). - –

24

Учитывая функции массива, который все возвращающие обещания, вы можете использовать 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'); 
}); 
+2

Хорошая работа с использованием 'reduce' –

+0

Это самый оптовый ответ – user2954463

+0

Любые мысли о том, как избежать потери памяти, вызванной' reduce'? –

0

Это решение основано на обещаниях использования введенных в ECMAScript 6 (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise), поэтому перед использованием его видеть поддержки таблиц browser`s 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 

Примечания:

  1. Это не работает, если функция содержит таймер (для этой проблемы я стараюсь также jQuery`s $ Callbacks, $ .ajax и $ .При, но это не помогает. Единственное решение, что я нашел, использование resolve() в обратном вызове таймера , но это неприемлемо, если вы выполнили функции.).
  2. Тестирование среды

$ google-chrome --version 
Google Chrome 53.0.2785.116 
0

Это ES7 путь.

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

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

0

я просто была проблема с моим апи провайдера, который делает Promise.all() будет в конечном итоге в задачах параллелизм дб ..

Сделка с моей ситуации является то, что мне нужно получить результат каждого обещания, чтобы показать предупреждение «все нормально» или «кто-то получил ошибку».

И я не знаю, почему .. этот маленький кусочек кода, который использует уменьшить, когда обещания решить я не мог получить мой возможности для работы (слишком поздно, чтобы расследовать сейчас)

$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) { ... }); 

Я надеюсь, что это помогает.

Смежные вопросы