Como seqüencialmente sequencia promises com angularjs $ q?

Na biblioteca prometora Q , você pode fazer o seguinte para sequenciar as promises em cadeia:

var items = ['one', 'two', 'three']; var chain = Q(); items.forEach(function (el) { chain = chain.then(foo(el)); }); return chain; 

no entanto, o seguinte não funciona com $ q :

 var items = ['one', 'two', 'three']; var chain = $q(); items.forEach(function (el) { chain = chain.then(foo(el)); }); return chain; 

Redgeoff, sua própria resposta é a maneira que eu usei para traduzir uma matriz em uma série de promises encadeadas.

O padrão emergente de fato é o seguinte:

 function doAsyncSeries(arr) { return arr.reduce(function (promise, item) { return promise.then(function(result) { return doSomethingAsync(result, item); }); }, $q.when(initialValue)); } //then var items = ['x', 'y', 'z']; doAsyncSeries(items).then(...); 

Notas:

  • .reduce é javascript bruto, não faz parte de uma biblioteca.
  • result é o resultado / dados asynchronouss anteriores e está incluído para fins de conclusão. O result inicial é initialValue . Se não for necessário passar o resultado, simplesmente deixe de fora.
  • adapte $q.when(initialValue) dependendo de qual promise você usa.
  • no seu caso, doSomethingAsync é foo (ou o que foo () retorna?) – em qualquer caso, uma function.

Se você é como eu, então o padrão parecerá, à primeira vista, um ataque impenetrável, mas uma vez que seu olho esteja sintonizado, você começará a considerá-lo como um velho amigo.

Editar

Aqui está uma demonstração , projetada para demonstrar que o padrão recomendado acima de fato executa suas chamadas doSomethingAsync() sequencialmente, não imediatamente durante a construção da cadeia, como sugerido nos comentários abaixo.

Simplesmente use a function $ q.when ():

 var items = ['one', 'two', 'three']; var chain = $q.when(); items.forEach(function (el) { chain = chain.then(foo(el)); }); return chain; 

Nota: foo deve ser uma fábrica, por exemplo

 function setTimeoutPromise(ms) { var defer = $q.defer(); setTimeout(defer.resolve, ms); return defer.promise; } function foo(item, ms) { return function() { return setTimeoutPromise(ms).then(function () { console.log(item); }); }; } var items = ['one', 'two', 'three']; var chain = $q.when(); items.forEach(function (el, i) { chain = chain.then(foo(el, (items.length - i)*1000)); }); return chain; 
 var when = $q.when(); for(var i = 0; i < 10; i++){ (function() { chain = when.then(function() { return $http.get('/data'); }); })(i); } 

Tendo isto:

 let items = ['one', 'two', 'three']; 

Uma linha (bem, 3 para legibilidade):

 return items .map(item => foo.bind(null, item)) .reduce($q.when, $q.resolve()); 

De uma maneira talvez mais simples que a resposta do redgeoff , se você não precisar dela automatizada, você pode encadear promises usando $q.when() combinado com .then() como mostrado no começo deste post . return $q.when() .then(function(){ return promise1; }) .then(function(){ return promise2; });

Eu prefiro preparar funções que retornarão promises usando angular.bind (ou Function.prototype.bind ) e, em seguida, vinculá-las a uma cadeia usando atalho reduce. Por exemplo

 // getNumber resolves with given number var get2 = getNumber.bind(null, 2); var get3 = getNumber.bind(null, 3); [get2, get3].reduce(function (chain, fn) { return chain.then(fn); }, $q.when()) .then(function (value) { console.log('chain value =', value); }).done(); // prints 3 (the last value) 

Sua resposta está correta. No entanto, pensei em fornecer uma alternativa. Você pode estar interessado em $ q.serial se você se encontrar em série encadeando promises com frequência.

 var items = ['one', 'two', 'three']; var tasks = items.map(function (el) { return function () { foo(el, (items.length - i)*1000)); }); }); $q.serial(tasks); function setTimeoutPromise(ms) { var defer = $q.defer(); setTimeout(defer.resolve, ms); return defer.promise; } function foo(item, ms) { return function() { return setTimeoutPromise(ms).then(function () { console.log(item); }); }; } 
Intereting Posts