Proper While() Loop For Bluebird Promises (without Recursion?)
Solution 1:
It's not 100% clear what you're trying to do, but I'll write an answer that does the following things you mention:
- Loops until some condition in your code is met
- Allows you to use a delay between loop iterations
- Allows you to get and process the final result
- Works with Bluebird (I'll code to the ES6 promise standard which will work with Bluebird or native promises)
- Does not have stack build-up
First, let's assume you have some async function that returns a promise whose result is used to determine whether to continue looping or not.
functiongetNextItem() {
returnnewPromise.delay(Math.random()*1000).then(function() {
return(Math.floor(Math.random() * 1000));
});
}
Now, you want to loop until the value returned meets some condition
functionprocessLoop(delay) {
returnnewPromise(function(resolve, reject) {
var results = [];
functionnext() {
getNextItem().then(function(val) {
// add to result array
results.push(val);
if (val < 100) {
// found a val < 100, so be done with the loopresolve(results);
} else {
// run another iteration of the loop after delaysetTimeout(next, delay);
}
}, reject);
}
// start first iteration of the loopnext();
});
}
processLoop(100).then(function(results) {
// process results here
}, function(err) {
// error here
});
If you wanted to make this more generic so you could pass in the function and comparison, you could do this:
functionprocessLoop(mainFn, compareFn, delay) {
returnnewPromise(function(resolve, reject) {
var results = [];
functionnext() {
mainFn().then(function(val) {
// add to result array
results.push(val);
if (compareFn(val))
// found a val < 100, so be done with the loopresolve(results);
} else {
// run another iteration of the loop after delayif (delay) {
setTimeout(next, delay);
} else {
next();
}
}
}, reject);
}
// start first iteration of the loopnext();
});
}
processLoop(getNextItem, function(val) {
return val < 100;
}, 100).then(function(results) {
// process results here
}, function(err) {
// error here
});
Your attempts at a structure like this:
return getNextItem() !== false;
Can't work because getNextItem()
returns a promise which is always !== false
since a promise is an object so that can't work. If you want to test a promise, you have to use .then()
to get its value and you have to do the comparson asynchronously so you can't directly return a value like that.
Note: While these implementations use a function that calls itself, this does not cause stack build-up because they call themselves asynchronously. That means the stack has already completely unwound before the function calls itself again, thus there is no stack build-up. This will always be the case from a .then()
handler since the Promise specification requires that a .then()
handler is not called until the stack has returned to "platform code" which means it has unwound all regular "user code" before calling the .then()
handler.
Using async
and await
in ES7
In ES7, you can use async and await to "pause" a loop. That can make this type of iteration a lot simpler to code. This looks structurally more like a typical synchronous loop. It uses await
to wait on promises and because the function is declared async
, it always returns a promise:
functiondelay(t) {
returnnewPromise(resolve => {
setTimeout(resolve, t);
});
}
asyncfunctionprocessLoop(mainFn, compareFn, timeDelay) {
var results = [];
// loop until condition is metwhile (true) {
let val = awaitmainFn();
results.push(val);
if (compareFn(val)) {
return results;
} else {
if (timeDelay) {
awaitdelay(timeDelay);
}
}
}
}
processLoop(getNextItem, function(val) {
return val < 100;
}, 100).then(function(results) {
// process results here
}, function(err) {
// error here
});
Post a Comment for "Proper While() Loop For Bluebird Promises (without Recursion?)"