How can I perform dynamic chaining in Javascript Promises, all the time I have seen only hardcoding of the calls for eg., (promise).then(request/functionName).then(request/functionName)
Given an array functions that all return promises, you can use reduce() to run them sequentially:
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"
});
The example above uses ES6 Promises but all promise libraries have similar features.
Also, creating the array of promise returning functions is usually a good candidate for using map(). For example:
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');
});
One option is to utilize the properties of objects and the ability to invoke them via strings.
I wrote a small sample Here and posted it below.
The idea is that you have the set of functions that you wish to run set in some namespace or object, as I did in 'myNamespace':
myNamespace = {
"A": function() {return "A Function";},
"B": function() {return "B Function";},
"C": function() {return "C Function";}
}
Then your main promise would run and somehow (via inputs, ajax, prompts, etc.) you would get the string value of the function you want to have run, which isn't known until runtime:
My main promise uses a prompt to get a letter from the user:
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);
}
In the next 'then' I use that value (passed via the resolve function) to invoke the function:
.then(function(response) {
funcToRun = myNamespace[response]();})
Finally, I output to html the result of my dynamic function call and I use some recursive fun to make it more interactive and demonstrate that it is dynamic:
.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>
Since promises unwrap, just continue to add then statements and it will continue to be chained together
function asyncSeries(fns) {
return fns.reduce(function(p, fn) {
return p.then(fn);
}, Promise.resolve());
}
Recursively is a pretty cool way to do it as well :)
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);
});
}
}
This is ES7 way.
Let's say you have multiple promises defined in an array.
var funcs = [
_ => new Promise(res => setTimeout(_ => res("1"), 1000)),
_ => new Promise(res => setTimeout(_ => res("2"), 1000))
}
And you want to call like this.
chainPromises(funcs).then(result => console.log(result));
You can use async and await for this purpose.
async function chainPromises(promises) {
for (let promise of promises) { // must be for (.. of ..)
await promise();
}
}
This will execute the given functions sequentially(one by one), not in parallel. The parameter promises is an array of functions, which return Promise.
Plunker: http://plnkr.co/edit/UP0rhD?p=preview
I think the simplest way is:
const executePromises = function(listOfProviders){
const p = Promise.resolve(null);
for(let i = 0; i < listOfProviders.length; i++){
p = p.then(v => listOfProviders[i]());
}
return p;
};
I believe the above is basically equivalent to:
const executePromises = async function(listOfProviders) {
for(let i = 0; i < listOfProviders.length; i++){
await listOfProviders[i]();
}
};
This solution based on usage promises of introduced in the EcmaScript 6 (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise), so before use it see table browser`s support https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise#Browser_compatibility
Code
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);
Result in Chrome console (values seconds will be different):
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
Notes:
It is does not work if a function contains a timer (for this problem
I try also jQuery`s $Callbacks, $.Ajax and $.When but it not help.
The only decision, what I found, usage resolve() in callback of a
timer, but it is not acceptable if you have completed functions.).
Testing environment
$ google-chrome --version
Google Chrome 53.0.2785.116
I just had a problem with my api provider that doing Promise.all() would end up in concurrency db problems..
The deal with my situation is that i need to get every promise result in order to show some "all ok" or "some got error" alert.
And i don't know why .. this little piece of code who uses reduce when the promises resolved i couldn't get my scope to work (too late to investigate now)
$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());
}
So thanks to this post http://hellote.com/dynamic-promise-chains/ I came with this little bastard.. It's not polished but it's working all right.
$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);
});
}
Then i invoke the function like this:
var recurseFunction = $scope.recurse(promises, promises.length, results);
recurseFunction.then(function (response) { ... });
I hope it helps.
To build on this answer, if you want to return the result of all those promises, too:
async function chainPromises(promises) {
let results = [];
for (let promise of promises) {
let result = await promise();
results.push(result);
}
return results;
}
Check the following tutorial for
programmatic (dynamic) chaining of javascript/node.js promises and
Promise chaining using recursive functions
Programmatic-Chaining-and-Recursive-Functions-with-JavaScript-Promise
Related
My question is very similar to this, but I can't get it to work :(
PROBLEM:
I have a Javascript program that needs to run in IE11 and Chrome.
It has a list of functions that I need to execute in order.
Each function returns a Promise. Promise.all(promisesArray) works to the extent that I can "wait" until all the functions finish before proceeding. But it DOESN'T guarantee that the functions each run in order. This is essential.
I've tried using Array.prototype.reduce(), but I haven't been able to figure out how to use it correctly.
Because I need to run in IE11, I can't use ES6 features like "arrow functions".
Here is my code:
<script>
var fn = function(arg) {
return new Promise (function (resolve, reject) {
console.log("fn(" + arg + ")...");
resolve("OK");
});
}
var p = fn("A").then(function(r) {
console.log("promise resolved: " + r + ".");
})
var chain = [];
chain.push(fn("3"));
chain.push(fn("1"));
chain.push(fn("2"));
console.log("Built chain:", chain);
Promise.all(chain);
chain.length = 0;
chain[2] = fn("30");
chain[1] = fn("20");
chain[0] = fn("10");
chain.reduce(function(promise, item) {
return promise.then(function() {
console.log("then:", item);
}), Promise.resolve();
});
console.log("Done.");
</script>;
I need the functions to execute in order array[0], array[1], array[2].
You're really close with your reducer!
The initial value for the reducer is Promise.resolve(), so when the function is called the first time:
chain.reduce(function(promise, item) {
// ^^ promise is a resolved promise,
// ^^ item is chain[0]
return promise.then(function() {
console.log("then:", item);
// ^^ when the promise resolves (immediately) this is called
// BUT no value is returned.
}), Promise.resolve();
});
Compare this with manually chaining the promises. You'd return the next promise to wait for:
Promise.resolve()
.then(item => { console.log("then: ", item); return fn("10"); })
.then(item => { console.log("then: ", item); return fn("20"); })
.then(item => { console.log("then: ", item); return fn("30"); })
See how the reducer is so close? We'll just want to return a promise:
var chain = [fn("10"), fn("20"), fn("30")];
chain.reduce(function(promise, item) {
return promise.then(function() {
console.log("then:", item);
return item;
}), Promise.resolve();
});
Edit:
If the fn call starts the work, each of these assignments will start the calls out of order:
chain.length = 0;
chain[2] = fn("30");
chain[1] = fn("20");
chain[0] = fn("10");
To run each fn in the order you want, you'll have to defer the call to fn until the previous call resolves. We did that in our manual example above. Depending on how complex your data is, you could reduce over the array of arguments for fn or wrap each call in a function that won't run:
[10,20,30]
.reduce(function(promise, arg) {
return promise.then(function() {
return fn(arg);
}), Promise.resolve();
});
[function() { return fn(10); }, function() { return fn(20) }, function() { return fn(30) }]
.reduce(function(promise, callFn) {
return promise.then(function() {
return fn(callFn);
}), Promise.resolve();
});
Well, I have the code below:
forEach(elements, element =>{
if(condition){
doSomething(element)
} else {
doSomethingElse(element)
}
})
I would like make this in an async way, meaning the code waits until the operation performed on that element is completed before passing on to the next element.
I would appreciate your help thanks!
.forEach() is already synchronous (meaning it acts on one element at a time), just use it as described here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
Your syntax is a bit off, it should look something like this (.forEach() is called as a method on the array it is operating on):
elements.forEach(element => {
if (condition) {
doSomething(element)
} else {
doSomethingElse(element)
}
})
You can use async / await. Assuming your methods return a Promise, you can do this:
const doSomething = () => {
return new Promise(resolve => { resolve('do something done!') });
}
const doSomethingElse = () => {
return new Promise(resolve => { resolve('do something else done!') });
}
const arr = [ 1, 2, 3, 4 ];
arr.forEach(async (item, i) => {
let result = null;
if (i % 2 === 0) { // or any other condition
result = await doSomething();
} else {
result = await doSomethingElse();
}
console.log(result);
});
Because you should avoid resolving promises in a for loop, as I said in comment you should store your promises in an array and resolve all of them after your loop. This should looks like this :
const promises = [];
elements.forEach(element =>{
promises.push(condition ? doSomething(element) : doSomethingElse(element));
});
Promise.all(promises).then(...).catch(...);
This is only correct if doSomething and doSomethingElse are async functions.
None of the answers provided thus far actually execute in the one at a time way you're looking for.
Promises invoke their executor function upon construction. So, by nature, in order to use Promise.all, all of your async operations must already be running. Think of it as Promise.allAtOnce.
#Daniel's answer comes close, but Array#forEach executes synchronously, so it still runs all at once:
const wait = (time, message) => new Promise(resolve => {
setTimeout(_=>{
console.log(message);
resolve();
}, time);
});
let promises = [];
[3000, 2000, 1000].forEach(
(time, index) => promises.push(wait(time, ++index))
)
Promise.all(promises);
/*
Even though `3000` was the first item,
`1000` resolves first because all the `Promise`
executors run at once.
Output:
3
2
1
*/
The most succinct way to execute one at a time is with an async loop:
const wait = (time, message) => new Promise(resolve => {
setTimeout(_=>{
console.log(message);
resolve();
}, time);
});
const iterate = async _ => {
let index = 0;
for (let time of [3000, 2000, 1000]) try {
await wait(time, ++index)
} catch (e) {
console.error(e);
}
}
iterate();
/*
Even though `3000` takes 3X longer to resolve,
the `wait` of `3000` resolves first,
because it is first in the iteration.
Output:
1
2
3
*/
If you want to avoid using async/await, just chain the promises with a loop. This will produce the same result:
const wait = (time, message) => new Promise(resolve => {
setTimeout(_=>{
console.log(message);
resolve();
}, time);
});
const iterate = _ => {
// initiate with an already-resolved promise
let lastPromise = Promise.resolve();
let index = 0;
for (let time of [3000, 2000, 1000]) {
// chain each promise to the promise returned by the previous item.
lastPromise = lastPromise
.then(result => wait(time, ++index))
.catch(console.error);
}
return lastPromise;
}
iterate();
/*
Even though `3000` takes 3X longer to resolve,
the `wait` of `3000` resolves first,
because it is first in the iteration.
Output:
1
2
3
*/
Here's my attempt, using recursive function:
var i = 0, n = elements.length;
function doNext() {
// exit if all elements have been accounted for
if (i >= n) {
return;
}
var element = elements[i];
i++;
if (condition) {
doSomething(element).then(doNext);
} else {
doSomethingElse(element).then(doNext);
}
}
I'm using Array.forEach function of javascript to get a different message for each element of a list. So I'm using forEach function and I'm looking for a way to execute my callback function cb(result) when the foreach as finished to both execute .forEach and msgAfterTimeout. I read that there was something called promises but I don't really get how I can use them here.
function msgAfterTimeout (who, timeout, onDone) {
setTimeout(function () {
onDone(" Hello " + who + "!");
}, timeout);
}
var test="";
var list = [{name:"foo",surname:"bar"},{name:"Jean",surname:"dupond"}];
function dispName(cb)
{
list.forEach(function(item, index)
{
msgAfterTimeout(item.name, 200, function (msg)
{
test=msg+"\n";
});
cb(result);
});
}
dispName(function(data){
console.log(data);
});
Here's your example with Promises:
var list = [{name: "foo", surname: "bar"}, {name: "Jean", surname: "dupond"}];
function msgAfterTimeout(who, timeout) {
return new Promise(resolve =>
setTimeout(
() => resolve(" Hello " + who + "!"),
timeout)
);
}
Promise.all(
list.map(item => msgAfterTimeout(item.name, 200))
).then(
result => console.log(result.join('\n'))
);
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
function msgAfterTimeout (who, timeout, onDone) {
setTimeout(function () {
onDone(" Hello " + who + "!");
}, timeout);
}
var test= "";
var list = [{name:"foo",surname:"bar"},{name:"Jean",surname:"dupond"}];
function newFunc(list, i, endCb) {
var name = list[i].name;
msgAfterTimeout(name, 200, function(msg) {
var test = msg + "\n";
console.log(test);
if(i+1 == list.length) {
endCb();
}
else {
newFunc(list, i+1, endCb);
}
});
}
function dispName(cb)
{
newFunc(list, 0 , cb);
}
dispName(function(){
console.log("Done with all!");
});
This outputs:
Hello foo!
Hello Jean!
Done with all!
forEach processes things in series, so for the sake of this answer, let's assume that's a requirement. Promise.all as demonstrated by #georg will process the items in parallel – it's a good answer but it won't help you if a series is what you actually need.
The Array.prototype methods reduce, map, and forEach are synchronous, but you could easily make an asynchronous variants. You're using forEach here but you're actually performing a reduce by hand. So we'll start by implementing an asynchronous reducek and then go from there
Don't worry too much about understanding reducek itself. All you need to know is the primary difference between reducek and reduce is that the reducing function receives an extra callback argument that is called when each item is done, and reducek has its own callback for when the entire input list is done.
function reducek (f, acc, [x, ...xs], k) {
if (x === undefined)
return k (acc)
else
return f (acc, x, y => reducek (f, y, xs, k))
}
function msgAfterTimeout (who, timeout, onDone) {
return setTimeout(onDone, timeout, "Hello " + who + "!")
}
function messageReducer (acc, item, done) {
return msgAfterTimeout(item.name, 200, function (text) {
return done([...acc, text])
})
}
function displayName (list, done) {
// use our async reduce here
return reducek (messageReducer, [], list, done)
}
var list = [{name:"foo",surname:"bar"},{name:"Jean",surname:"dupond"}]
displayName (list, function (texts) {
console.log("Done!", texts.join(' '))
})
// Done! Hello foo! Hello Jean!
Important things to notice ...
No longer performing reduce by hand – instead of using a test variable initialized with state of '' (empty string), reducek accepts the initial value as an argument.
Here we're using an initial state of of [] (empty array) to store each of the texts. When we're done, we join the texts together using texts.join(' ').
Not Another Ceremony ...
All of that is a lot of ceremony and continuations are not necessarily the best for asynchronous flow control. In fact, Promises were brought to JavaScript because a better tool was needed.
// instead of returning a timeout, return a Promise
function msgAfterTimeout (who, timeout) {
return new Promise(function (resolve) {
setTimeout(resolve, timeout, "Hello " + who + "!")
})
}
// new async/await syntax - work with Promises in a most elegant fashion
// no more callback parameter; async implicitly returns a Promise
async function displayName (list) {
let texts = []
for (let item of list)
texts.push(await msgAfterTimeout(item.name, 200))
return texts
}
var list = [{name:"foo",surname:"bar"},{name:"Jean",surname:"dupond"}]
// instead of a callback, chain a .then on the returned Promise
displayName(list).then(texts => console.log("Done!", texts.join(' ')))
// Done! Hello foo! Hello Jean!
Note: If you need to support older browsers, async/await needs to be transpiled using something like Babel.
How to correctly construct a loop to make sure the following promise call and the chained logger.log(res) runs synchronously through iteration? (bluebird)
db.getUser(email).then(function(res) { logger.log(res); }); // this is a promise
I tried the following way (method from http://blog.victorquinn.com/javascript-promise-while-loop )
var Promise = require('bluebird');
var promiseWhile = function(condition, action) {
var resolver = Promise.defer();
var loop = function() {
if (!condition()) return resolver.resolve();
return Promise.cast(action())
.then(loop)
.catch(resolver.reject);
};
process.nextTick(loop);
return resolver.promise;
});
var count = 0;
promiseWhile(function() {
return count < 10;
}, function() {
return new Promise(function(resolve, reject) {
db.getUser(email)
.then(function(res) {
logger.log(res);
count++;
resolve();
});
});
}).then(function() {
console.log('all done');
});
Although it seems to work, but I don't think it guarantees the order of calling logger.log(res);
Any suggestions?
If you really want a general promiseWhen() function for this and other purposes, then by all means do so, using Bergi's simplifications. However, because of the way promises work, passing callbacks in this way is generally unnecessary and forces you to jump through complex little hoops.
As far as I can tell you're trying :
to asynchronously fetch a series of user details for a collection of email addresses (at least, that's the only scenario that makes sense).
to do so by building a .then() chain via recursion.
to maintain the original order when handling the returned results.
Defined thus, the problem is actually the one discussed under "The Collection Kerfuffle" in Promise Anti-patterns, which offers two simple solutions :
parallel asynchronous calls using Array.prototype.map()
serial asynchronous calls using Array.prototype.reduce().
The parallel approach will (straightforwardly) give the issue that you are trying to avoid - that the order of the responses is uncertain. The serial approach will build the required .then() chain - flat - no recursion.
function fetchUserDetails(arr) {
return arr.reduce(function(promise, email) {
return promise.then(function() {
return db.getUser(email).done(function(res) {
logger.log(res);
});
});
}, Promise.resolve());
}
Call as follows :
//Compose here, by whatever means, an array of email addresses.
var arrayOfEmailAddys = [...];
fetchUserDetails(arrayOfEmailAddys).then(function() {
console.log('all done');
});
As you can see, there's no need for the ugly outer var count or it's associated condition function. The limit (of 10 in the question) is determined entirely by the length of the array arrayOfEmailAddys.
I don't think it guarantees the order of calling logger.log(res);
Actually, it does. That statement is executed before the resolve call.
Any suggestions?
Lots. The most important is your use of the create-promise-manually antipattern - just do only
promiseWhile(…, function() {
return db.getUser(email)
.then(function(res) {
logger.log(res);
count++;
});
})…
Second, that while function could be simplified a lot:
var promiseWhile = Promise.method(function(condition, action) {
if (!condition()) return;
return action().then(promiseWhile.bind(null, condition, action));
});
Third, I would not use a while loop (with a closure variable) but a for loop:
var promiseFor = Promise.method(function(condition, action, value) {
if (!condition(value)) return value;
return action(value).then(promiseFor.bind(null, condition, action));
});
promiseFor(function(count) {
return count < 10;
}, function(count) {
return db.getUser(email)
.then(function(res) {
logger.log(res);
return ++count;
});
}, 0).then(console.log.bind(console, 'all done'));
Here's how I do it with the standard Promise object.
// Given async function sayHi
function sayHi() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('Hi');
resolve();
}, 3000);
});
}
// And an array of async functions to loop through
const asyncArray = [sayHi, sayHi, sayHi];
// We create the start of a promise chain
let chain = Promise.resolve();
// And append each function in the array to the promise chain
for (const func of asyncArray) {
chain = chain.then(func);
}
// Output:
// Hi
// Hi (After 3 seconds)
// Hi (After 3 more seconds)
Given
asyncFn function
array of items
Required
promise chaining .then()'s in series (in order)
native es6
Solution
let asyncFn = (item) => {
return new Promise((resolve, reject) => {
setTimeout( () => {console.log(item); resolve(true)}, 1000 )
})
}
// asyncFn('a')
// .then(()=>{return async('b')})
// .then(()=>{return async('c')})
// .then(()=>{return async('d')})
let a = ['a','b','c','d']
a.reduce((previous, current, index, array) => {
return previous // initiates the promise chain
.then(()=>{return asyncFn(array[index])}) //adds .then() promise for each item
}, Promise.resolve())
There is a new way to solve this and it's by using async/await.
async function myFunction() {
while(/* my condition */) {
const res = await db.getUser(email);
logger.log(res);
}
}
myFunction().then(() => {
/* do other stuff */
})
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
https://ponyfoo.com/articles/understanding-javascript-async-await
Bergi's suggested function is really nice:
var promiseWhile = Promise.method(function(condition, action) {
if (!condition()) return;
return action().then(promiseWhile.bind(null, condition, action));
});
Still I want to make a tiny addition, which makes sense, when using promises:
var promiseWhile = Promise.method(function(condition, action, lastValue) {
if (!condition()) return lastValue;
return action().then(promiseWhile.bind(null, condition, action));
});
This way the while loop can be embedded into a promise chain and resolves with lastValue (also if the action() is never run). See example:
var count = 10;
util.promiseWhile(
function condition() {
return count > 0;
},
function action() {
return new Promise(function(resolve, reject) {
count = count - 1;
resolve(count)
})
},
count)
I'd make something like this:
var request = []
while(count<10){
request.push(db.getUser(email).then(function(res) { return res; }));
count++
};
Promise.all(request).then((dataAll)=>{
for (var i = 0; i < dataAll.length; i++) {
logger.log(dataAll[i]);
}
});
in this way, dataAll is an ordered array of all element to log. And log operation will perform when all promises are done.
First take array of promises(promise array) and after resolve these promise array using Promise.all(promisearray).
var arry=['raju','ram','abdul','kruthika'];
var promiseArry=[];
for(var i=0;i<arry.length;i++) {
promiseArry.push(dbFechFun(arry[i]));
}
Promise.all(promiseArry)
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log(error);
});
function dbFetchFun(name) {
// we need to return a promise
return db.find({name:name}); // any db operation we can write hear
}
Use async and await (es6):
function taskAsync(paramets){
return new Promise((reslove,reject)=>{
//your logic after reslove(respoce) or reject(error)
})
}
async function fName(){
let arry=['list of items'];
for(var i=0;i<arry.length;i++){
let result=await(taskAsync('parameters'));
}
}
function promiseLoop(promiseFunc, paramsGetter, conditionChecker, eachFunc, delay) {
function callNext() {
return promiseFunc.apply(null, paramsGetter())
.then(eachFunc)
}
function loop(promise, fn) {
if (delay) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, delay);
})
.then(function() {
return promise
.then(fn)
.then(function(condition) {
if (!condition) {
return true;
}
return loop(callNext(), fn)
})
});
}
return promise
.then(fn)
.then(function(condition) {
if (!condition) {
return true;
}
return loop(callNext(), fn)
})
}
return loop(callNext(), conditionChecker);
}
function makeRequest(param) {
return new Promise(function(resolve, reject) {
var req = https.request(function(res) {
var data = '';
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
resolve(data);
});
});
req.on('error', function(e) {
reject(e);
});
req.write(param);
req.end();
})
}
function getSomething() {
var param = 0;
var limit = 10;
var results = [];
function paramGetter() {
return [param];
}
function conditionChecker() {
return param <= limit;
}
function callback(result) {
results.push(result);
param++;
}
return promiseLoop(makeRequest, paramGetter, conditionChecker, callback)
.then(function() {
return results;
});
}
getSomething().then(function(res) {
console.log('results', res);
}).catch(function(err) {
console.log('some error along the way', err);
});
How about this one using BlueBird?
function fetchUserDetails(arr) {
return Promise.each(arr, function(email) {
return db.getUser(email).done(function(res) {
logger.log(res);
});
});
}
Here's another method (ES6 w/std Promise). Uses lodash/underscore type exit criteria (return === false). Note that you could easily add an exitIf() method in options to run in doOne().
const whilePromise = (fnReturningPromise,options = {}) => {
// loop until fnReturningPromise() === false
// options.delay - setTimeout ms (set to 0 for 1 tick to make non-blocking)
return new Promise((resolve,reject) => {
const doOne = () => {
fnReturningPromise()
.then((...args) => {
if (args.length && args[0] === false) {
resolve(...args);
} else {
iterate();
}
})
};
const iterate = () => {
if (options.delay !== undefined) {
setTimeout(doOne,options.delay);
} else {
doOne();
}
}
Promise.resolve()
.then(iterate)
.catch(reject)
})
};
Using the standard promise object, and having the promise return the results.
function promiseMap (data, f) {
const reducer = (promise, x) =>
promise.then(acc => f(x).then(y => acc.push(y) && acc))
return data.reduce(reducer, Promise.resolve([]))
}
var emails = []
function getUser(email) {
return db.getUser(email)
}
promiseMap(emails, getUser).then(emails => {
console.log(emails)
})
I have an async function like:
$scope.get_device_version = function(){
return $q(function(resolve, reject){
$http.get('/api/get_version')
.then(function(response) {
resolve(response.data.version);
},function(response) {
reject("Lost connection..");
});
});
};
Question 1: Now I want to run that function 10 times in a row sequentially, what do I do?
Question 2: Now I want to run the function until I get a desired answer from the http-request, what do I do?
Question 1
.then(callback) returns a new promise resolving to the return value of callback, or if the return value is another promise, to the settled value of that promise. This can be used to chain asynchronous calls. For example, given
function myFunction() { return promise; }
the following snippet will call myFunction 10 times sequentially
var result = $q.resolve();
for (var i = 0; i < 10; ++i) {
result = result.then(myFunction);
}
result.then(function () { /* myFunction has been called 10 times */ })
Question 2
Here, recursion is required since you don't know the exact number of times you will need to iterate. For example, given myFunction as above,
function wrapper() {
var deferred = $q.defer();
iter();
function iter(res) {
// skip first iter() call, wait for acceptable res
if (result !== void 0 && /* res is what I want */) {
return deferred.resolve(res);
}
// recurse
return myFunction().then(iter);
}
return deferred.promise;
}
wrapper().then(function (res) { /* res is an acceptable response */ })
Note that for this use case, promises do not really offer an advantage over simple callbacks.
Question 1:
var request = null;
for( var i = 0; i < 10; i++ ) {
if( request )
request.then( $scope.get_device_version );
else
request = $scope.get_device_version();
}
Question 2:
$scope.get_device_version = function(){
return $q(function(resolve, reject){
$http.get('/api/get_version')
.then(function(response) {
if( /*Not a desired answer? run again:*/ )
$scope.get_device_version()
else
resolve(response.data.version);
},function(response) {
reject("Lost connection..");
});
});
};
This is pseudo code, i have not tested it.
var checkDeviceVersion=function (times) {
$scope.get_device_version().then(function(data) {
if(times==0) return;
checkDeviceVersion(times-1);
});
}
var keepChecking=function () {
$scope.get_device_version().then(function(data) {
if(data.condition) return;
keepChecking();
});
}
Invoke then as checkDeviceVersion(10) or keepChecking()