So i am trying to transfer my code into the "Promise world", and in many places when i had to "loop" with async functionality - i simply used recursion in such a way
function doRecursion(idx,callback){
if(idx < someArray.length){
doAsync(function(){
doRecursion(++idx,callback)
});
}else{
callback('done!')
}
}
doRecursion(0,function(msg){
//...
});
Now i am trying to make the change into the Promise world, and i am quite stuck
var Promise = require('bluebird')
function doRecursion(idx){
return new Promise(function(resolve){
if(idx < someArray.length){
doAsync(function(){
//... doRecursion(++idx)
// how do i call doRecusion here....
});
}else{
resolve('done!')
}
});
}
doRecursion(0).then(function(msg){
//...
});
Thanks.
I'd go with the Promise.all approach.
What this does is wait until all the promises in the array have resolved. The map will apply the async method to each item in the array and return a promise.
function doAsyncP() {
return new Promise((resolve) => {
doAsync(function() {
resolve();
});
});
}
Promise.all(
someArray.map(doAsyncP)
).then((msg) => {
//we're done.
});
In your recursive function, you can do this:
...
if (idx < someArray.length) {
doAsync(function() {
resolve(doRecursion(idx + 1));
});
} else {
...
In other words, while idx is less than someArray.length, your promise will resolve to another promise, this time the promise returned by calling doRecursion() with an idx incremented by one. The then callback the bottom will not be called until doRecursion resolves to some value other than a promise. In this case, it will eventually resolve with a value of 'done!'.
That said, if you are using promises, you probably don't need to use recursion at all. You might have to refactor your code a bit more, but I would suggest considering #BenFortune's answer as an alternative.
Related
I'm making a bot code for a game running on NodeJS and what this function is supposed to do is to loop through an array of vectors and then make the bot go to each vector.
However, what it's actually doing is telling the bot to run to all the vectors at the same time so it spazzes out and then runs to the last vector in the array:
function digSchedule() {
var arrayLength = blocksToMine.length;
for (var i = 0; i < blocksToMine.length; i++) {
console.log(i);
scaffoldTo(blocksToMine[i]);
}
...
}
The function scaffoldTo() needs to be ran and then wait for the bot to do said function then run it for the next element in the array, but I can't figure out how to do it.
There's several ways to achieve this. The first is probably to pass a callback with the "next function to be called" (probably scaffoldTo()). You can use .bind() to create a reference with the iterator index i.
Alternatively, you could set up a loop of Promises, which by definition have a .then() method which executes once the promise is resolved.
Finally, the async/await pattern is similar to Promises, but some find it clearer and it seems to be winning the Hype Wars: https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9.
Callbacks (solution 1) will be available in any version of JS. Promises generally available with a library and have native support in ES6. Async/await is a proposal (?) in ES2017 and is generally well supported.
Here's another way that you could play with this. This way focuses more on the callback style, although it does assume that you can modify the scaffoldTo function, as well as the parameters required for digSchedule
function digSchedule(i, callback) {
if(!i){
i = 0;
}
if(i < blocksToMine.length){
scaffoldTo(blocksToMine[i], digSchedule(i++, callback));
}
else{
callback();
}
}
Then inside of scaffoldTo you would need something like this
function scaffoldTo(block, callback){
//do what ever you need to have the bot go to the vector
//after bot goes to vector call callback
callback();
}
In order to start it all you would just need to call digSchedule with something like this:
digSchedule({null or 0}, function(){
console.log("finished visiting vectors");
});
This does change the pattern from using a for loop like you have, but I think its a fun way to accomplish the goal as well.
That is a good use case to the async functions of ES2017.
Please, try the following:
async function digSchedule() {
var arrayLength = blocksToMine.length;
for (var i = 0; i < blocksToMine.length; i++) {
console.log(i);
await scaffoldTo(blocksToMine[i]);
}
...
}
If ES2017 is out of the question, your best choice would be to make a recursive function that only calls itself again when the promise from scaffoldTo is resolved.
You may use async module to achieve this. Alternatively, you may try something like this
function forEachAsync(array, fun, cb) {
var index = 0;
if (index == array.length) {
cb(null);
return;
}
var next = function () {
fun(array[index], function(err) {
if (err) {
cb(err);
return;
}
index++;
if (index < array.length) {
setImmediate(next);
return;
}
//We are done
cb(null);
});
};
next();
}
forEachAsync([1,2,3,4,5], function(e, cb) {
console.log(e);
cb();
}, function(err) {
console.log('done');
});
Here is how we do with the help of promises.
let numbers = new Array(1,3,2,1);
function asyncFunction(number){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("Number : ",number);
return resolve();
},number*1000);
})
}
let promise = Promise.resolve();
// Here we are using forEach to chain promise one after another.
numbers.forEach(number=>{
promise = promise.then(()=>{
return asyncFunction(number);
});
})
promise.then(()=>{
console.log("Every thing is done!");
})
I have a standard JavaScript project. It is mostly synchronous with a few callbacks. I needed to use a 3rd party library to simplify things. The issue is that this library is based on an async approach and Promises. I have never really used this approach before.
I have a situation where I just need something = get_info(); The code is all very modular, so there are multiple function calls stacked up. The problem is this get_info is async. I just can't figure out how to use this async function withing my project without having to rewrite the whole thing.
Edit: Here is an example of similar code
function my_class() {
this.do_something( data ) {
if ( this.get_data() == data ) {
do_the_thing();
}
}
this.get_data = function() {
return library.get_info( arguments ); //can't do this, returns a promise
}
}
var stuff = new my_class();
for ( var i = 0; i < len; i++ ) {
stuff.do_something( i )
}
Again, the goal is to not rewrite this entire part of the application. Thoughts?
There is no need to promisify every function that gets called in the chain. You can call either synchronous or asynchronous functions as much as you want within the chain of the promises. All you have to think about is where to wait for the resolved value.
Your code isn't far off from working, a simple addition of a .then() block makes it work.
Working example below:
function my_class() {
this.do_something(data) {
//Call the promise-returning function
this.get_data()
.then((resolvedData) => {
//Wait for promise resolution
//and compare the resolved value to 'data'
if (resolvedData === data) {
do_the_thing();
}
})
.catch();
}
this.get_data = () => {
return library.get_info(arguments);
}
}
var stuff = new my_class();
for (var i = 0; i < len; i++) {
stuff.do_something(i)
}
How does it work?
do_something(i) is called
this.get_data() is called from inside the do_something() function.
When the promise that get_data() returns has been resolved, execution continues into then .then() block.
The resolvedData is the resolved value from the get_data() promise.
Compare the values, and continue.
As you can see, this does not take into consideration that the for-loop will continue even though the call to stuff.do_something(i) hasn't completed.
It is unclear in your question if you allow them all to run in parallel (like now) or if you need that they run in sequence.
If you want to to know if all calls are done, you can let do_something() return a promise and then wait for them all to resolve using Promise.all(), like below
function my_class() {
this.do_something(data) {
//Call the promise-returning function
//Note that we return the same promise - no need to create a new one!
return this.get_data()
.then((resolvedData) => {
//Wait for promise resolution
//and compare the resolved value to 'data'
if (resolvedData === data) {
do_the_thing();
}
})
.catch();
}
this.get_data = () => {
return library.get_info(arguments);
}
}
var stuff = new my_class();
//
var promises = [];
for (var i = 0; i < len; i++) {
//Push each promise to the array
promises.push(stuff.do_something(i));
}
Promise.all(promises)
.then(() => {
console.log("All promises have been resolved!")
})
.catch(() => {
//Handle errors
});
function my_class() {
this.do_something( data ) {
return new Promise(function(resolve,reject){
this.get_data().then(function(dataReceiced){
//reading promise retuned by get_data();
if(data == dataReceiced){
do_the_thing(); //need to promisify this also to use then();
resolve('Done');
} else { reject('Error'); }
});
});
}
}
this.get_data = function() {
return library.get_info( arguments ); //can't do this, returns a promise
}
}
i=0; // keep i global
var stuff = new my_class();
function doStuff(){
if(i < len){ mainStuff(); }
}
function mainStuff(){
stuff.do_something( i ).then(function(){
i++;
doStuff();
});
};
doStuff(); // to start process.
You need to promisify every function involved in the chain ..
Something like this..
Not sure about the complete scenario , code will look something like this.
I'm consuming an API that returns JSON, and on this page I have a progress bar indicating various steps toward setting something up at the user's request. Each subsequent AJAX request's success callback initiates the next request, because they need to be done in sequence. One step issues a server-side background job and the endpoint returns a transaction ID.
Outside this flow there is a function that checks another endpoint to see if this transaction is complete or not. If it's "pending", I need to reissue the request after a small delay.
I had this working with a recursive function:
function checkTransaction(trxid) {
window.profileTrx[trxid] = 0;
trxurl = 'https://example.com/transaction/'+trxid;
$.getJSON(trxurl,function(result) {
if(result.status === 'pending') {
setTimeout(function () {
checkTransaction(trxid);
},3000);
} else {
window.profileTrx[trxid] = result;
}
});
}
The reason I was using window is so I could access the transaction by its ID in the callback it came from - a good use case for a promise if ever there were one. But it got messy, and my lack of experience began to get in my way. Looping over the state of window.profileTrx[trxid] seemed like double work, and didn't behave as expected, looping too quickly and crashing the page. Again, a promise with the next step in .then() was my idea, but I can't figure out how.
How could I implement this with promises such that the callback function that initiated the recursive "transaction check" would only continue with the rest of its execution once the API returns a non-pending response to the check?
I could get my head round recursing, and returning a promise, but not both at once. Any and all help massively appreciated.
My head is always clearer when I factor out promises first:
// wrap timeout in a promise
function wait(ms) {
var deferred = $.Deferred();
setTimeout(function() {
deferred.resolve();
}, ms);
return deferred.promise();
}
// promise to get a trxid
function getTRX(trxid) {
var trxurl = 'https://example.com/transaction/'+trxid;
return $.getJSON(trxurl);
}
Now the original function seems easy...
function checkTransaction(trxid) {
window.profileTrx[trxid] = trxid;
return getTRX(trxid).then(function(result) {
if (result.status === 'pending') {
return wait(3000).then(function() {
return checkTransaction(trioxid);
});
} else {
window.profileTrx[trxid] = result;
return result;
}
});
}
The caller will look like this:
return checkTransaction('id0').then(function(result) {
return checkTransaction('id1');
}).then(function(result) {
return checkTransaction('id2');
}) // etc
Remember, if the checkTransaction stays pending for a very long time, you'll be building very long chains of promises. Make sure that the get returns in some very small multiple of 3000ms.
"deferred"-based solution (not recommended)
Since you are using jQuery in your question, I will first present a solution that uses jQuery's promise implementation based on the $.Deferred() object. As pointed out by #Bergi, this is considered an antipattern.
// for this demo, we will fake the fact that the result comes back positive
// after the third attempt.
var attempts = 0;
function checkTransaction(trxid) {
var deferred = $.Deferred();
var trxurl = 'http://echo.jsontest.com/status/pending?' + trxid;
function poll() {
console.log('polling...');
// Just for the demo, we mock a different response after 2 attempts.
if (attempts == 2) {
trxurl = 'http://echo.jsontest.com/status/done?' + trxid;
}
$.getJSON(trxurl, function(result) {
if (result.status === 'pending') {
console.log('result:', result);
setTimeout(poll, 3000);
} else {
deferred.resolve('final value!');
}
});
// just for this demo
attempts++;
}
poll();
return deferred.promise();
}
checkTransaction(1).then(function(result) {
console.log('done:', result)
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
This should work (run the snippet to see), but as mentioned in the linked answer, there are issues with this "deferred" pattern, such as error cases not being reported.
The issue is that jQuery promises (until possibly recent versions - I've not checked) have massive issues that prevent better patterns from being used.
Another approach would be to use a dedicated promise library, which implements correct chaining on then() functions, so you can compose your function in a more robust way and avoid the "deferred" antipattern:
Promise composition solution (better)
For real promise composition, which avoids using "deferred" objects altogether, we can use a more compliant promise library, such as Bluebird. In the snippet below, I am using Bluebird, which gives us a Promise object that works as we expect.
function checkTransaction(trxid) {
var trxurl = 'http://echo.jsontest.com/status/pending?' + trxid;
var attempts = 0;
function poll() {
if (attempts == 2) {
trxurl = 'http://echo.jsontest.com/status/done?' + trxid;
}
attempts++;
console.log('polling...');
// wrap jQuery's .getJSON in a Bluebird promise so that we
// can chain & compose .then() calls.
return Promise.resolve($.getJSON(trxurl)
.then(function(result) {
console.log('result:', result);
if (result.status === 'pending') {
// Bluebird has a built-in promise-ready setTimeout
// equivalent: delay()
return Promise.delay(3000).then(function() {
return poll();
});
} else {
return 'final value!'
}
}));
}
return poll();
}
checkTransaction(1).then(function(result) {
console.log('done:', result);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.4.1/bluebird.min.js"></script>
You can return promises from functions, and the .then of the parent function will resolve when all the returned promises are resolved.
check this out for full details.
https://gist.github.com/Bamieh/67c9ca982b20cc33c9766d20739504c8
I've been learning promises using bluebird for two weeks now. I have them mostly understood, but I went to go solve a few related problems and it seems my knowledge has fell apart. I'm trying to do this simple code:
var someGlobal = true;
whilePromsie(function() {
return someGlobal;
}, function(result) { // possibly even use return value of 1st parm?
// keep running this promise code
return new Promise(....).then(....);
});
as a concrete example:
// This is some very contrived functionality, but let's pretend this is
// doing something external: ajax call, db call, filesystem call, etc.
// Simply return a number between 0-999 after a 0-999 millisecond
// fake delay.
function getNextItem() {
return new Promise.delay(Math.random()*1000).then(function() {
Promise.cast(Math.floor(Math.random() * 1000));
});
}
promiseWhile(function() {
// this will never return false in my example so run forever
return getNextItem() !== false;
}, // how to have result == return value of getNextItem()?
function(result) {
result.then(function(x) {
// do some work ...
}).catch(function(err) {
console.warn("A nasty error occured!: ", err);
});
}).then(function(result) {
console.log("The while finally ended!");
});
Now I've done my homework! There is the same question, but geared toward Q.js here:
Correct way to write loops for promise.
But the accepted answers, as well as additional answers:
Are geared toward Q.js or RSVP
The only answer geared toward bluebird uses recursion. These seems like it's likely to cause a huge stack overflow in an infinite loop such as mine? Or at best, be very inefficient and create a very large stack for nothing? If I'm wrong, then fine! Let me know.
Don't allow you to use result of the condition. Although this isn't requirement -- I'm just curious if it's possible. The code I'm writing, one use case needs it, the other doesn't.
Now, there is an answer regarding RSVP that uses this async() method. And what really confuses me is bluebird documents and I even see code for a Promise.async() call in the repository, but I don't see it in my latest copy of bluebird. Is it in the git repository only or something?
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.
function getNextItem() {
return new Promise.delay(Math.random()*1000).then(function() {
return(Math.floor(Math.random() * 1000));
});
}
Now, you want to loop until the value returned meets some condition
function processLoop(delay) {
return new Promise(function(resolve, reject) {
var results = [];
function next() {
getNextItem().then(function(val) {
// add to result array
results.push(val);
if (val < 100) {
// found a val < 100, so be done with the loop
resolve(results);
} else {
// run another iteration of the loop after delay
setTimeout(next, delay);
}
}, reject);
}
// start first iteration of the loop
next();
});
}
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:
function processLoop(mainFn, compareFn, delay) {
return new Promise(function(resolve, reject) {
var results = [];
function next() {
mainFn().then(function(val) {
// add to result array
results.push(val);
if (compareFn(val))
// found a val < 100, so be done with the loop
resolve(results);
} else {
// run another iteration of the loop after delay
if (delay) {
setTimeout(next, delay);
} else {
next();
}
}
}, reject);
}
// start first iteration of the loop
next();
});
}
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:
function delay(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
async function processLoop(mainFn, compareFn, timeDelay) {
var results = [];
// loop until condition is met
while (true) {
let val = await mainFn();
results.push(val);
if (compareFn(val)) {
return results;
} else {
if (timeDelay) {
await delay(timeDelay);
}
}
}
}
processLoop(getNextItem, function(val) {
return val < 100;
}, 100).then(function(results) {
// process results here
}, function(err) {
// error here
});
I have a list of objects. The objects are passed to a deferred function. I want to call the function with the next object only after the previous call is resolved. Is there any way I can do this?
angular.forEach(objects, function (object) {
// wait for this to resolve and after that move to next object
doSomething(object);
});
Before ES2017 and async/await (see below for an option in ES2017), you can't use .forEach() if you want to wait for a promise because promises are not blocking. Javascript and promises just don't work that way.
You can chain multiple promises and make the promise infrastructure sequence them.
You can iterate manually and advance the iteration only when the previous promise finishes.
You can use a library like async or Bluebird that will sequence them for you.
There are lots of different alternatives, but .forEach() will not do it for you.
Here's an example of sequencing using chaining of promises with angular promises (assuming objects is an array):
objects.reduce(function(p, val) {
return p.then(function() {
return doSomething(val);
});
}, $q.when(true)).then(function(finalResult) {
// done here
}, function(err) {
// error here
});
And, using standard ES6 promises, this would be:
objects.reduce(function(p, val) {
return p.then(function() {
return doSomething(val);
});
}, Promise.resolve()).then(function(finalResult) {
// done here
}, function(err) {
// error here
});
Here's an example of manually sequencing (assuming objects is an array), though this does not report back completion or errors like the above option does:
function run(objects) {
var cntr = 0;
function next() {
if (cntr < objects.length) {
doSomething(objects[cntr++]).then(next);
}
}
next();
}
ES2017
In ES2017, the async/wait feature does allow you to "wait" for a promise to fulfill before continuing the loop iteration when using non-function based loops such as for or while:
async function someFunc() {
for (object of objects) {
// wait for this to resolve and after that move to next object
let result = await doSomething(object);
}
}
The code has to be contained inside an async function and then you can use await to tell the interpreter to wait for the promise to resolve before continuing the loop. Note, while this appears to be "blocking" type behavior, it is not blocking the event loop. Other events in the event loop can still be processed during the await.
Yes you can use angular.forEach to achieve this.
Here is an example (assuming objects is an array):
// Define the initial promise
var sequence = $q.defer();
sequence.resolve();
sequence = sequence.promise;
angular.forEach(objects, function(val,key){
sequence = sequence.then(function() {
return doSomething(val);
});
});
Here is how this can be done using array.reduce, similar to #friend00's answer (assuming objects is an array):
objects.reduce(function(p, val) {
// The initial promise object
if(p.then === undefined) {
p.resolve();
p = p.promise;
}
return p.then(function() {
return doSomething(val);
});
}, $q.defer());
check $q on angular:
function outerFunction() {
var defer = $q.defer();
var promises = [];
function lastTask(){
writeSome('finish').then( function(){
defer.resolve();
});
}
angular.forEach( $scope.testArray, function(value){
promises.push(writeSome(value));
});
$q.all(promises).then(lastTask);
return defer;
}
The easiest way is to create a function and manually iterate over all the objects in the array after each promise is resolved.
var delayedFORLoop = function (array) {
var defer = $q.defer();
var loop = function (count) {
var item = array[count];
// Example of a promise to wait for
myService.DoCalculation(item).then(function (response) {
}).finally(function () {
// Resolve or continue with loop
if (count === array.length) {
defer.resolve();
} else {
loop(++count);
}
});
}
loop(0); // Start loop
return defer.promise;
}
// To use:
delayedFORLoop(array).then(function(response) {
// Do something
});
Example is also available on my GitHub:
https://github.com/pietervw/Deferred-Angular-FOR-Loop-Example
I use a simple solution for a connection to a printer that wait till the promise is over to go to the next.
angular.forEach(object, function(data){
yourFunction(data)
.then(function (){
return;
})
})
It might help someone as I tried several of above solution before coming up with my own that actually worked for me (the other ones didn't)
var sequence;
objects.forEach(function(item) {
if(sequence === undefined){
sequence = doSomethingThatReturnsAPromise(item)
}else{
sequence = sequence.then(function(){
return doSomethingThatReturnsAPromise(item)
});
}
});
It worked for me like this. I don't know if it is a right approach but could help to reduce lines
function myFun(){
var deffer = $q.defer();
angular.forEach(array,function(a,i) {
Service.method(a.id).then(function(res) {
console.log(res);
if(i == array.length-1) {
deffer.resolve(res);
}
});
});
return deffer.promise;
}
myFun().then(function(res){
//res here
});