continue a for loop outside of a promise - javascript

so let's say I have something like this:
var x = 2;
for(var i = 0; i < 10; i++) {
new Promise(async (resolve, reject) => {
if(someFunctionThatChecksX(x)) {
continue;
}
}
}
I can't use continue because "jump target cannot cross function boundaries" and need a way around that to continue the for loop when that function that checks X results in true. Thanks so much for any assistance.

If you want the for loop to wait for the promise to resolve, put it inside of an async block. You could then use await to get the result of the promise, and continue based off that in the body of the for loop.
Altogether, it looks like this:
async function myFunction() {
var x = 2;
for(var i = 0; i < 10; i++) {
let result = await new Promise(async (resolve, reject) => {
resolve(someFunctionThatChecksX(x))
}
if(result) {
continue;
}
}
}

Related

async/await slower than callback

I'm trying to convert callbacks to async/await, but found that async/await is much slower than the existing callbacks. Can anyone see what's wrong with my async/await?
for (var i = 0; i < balance; i++) {
tokenOfOwnerByIndex().call(i).then((id) => {
tokenURI().call(id).then((uri) => {
console.log(uri);
});
});
}
for (var i = 0; i < balance; i++) {
var id = await this.getTokenOfOwnerByIndex(i);
var uri = await this.getTokenURI(id);
console.log(uri);
}
In the first version tokenOfOwnerByIndex is called returning a promise. You attach a callback via then, and the loop continues. The promise will eventually resolve, but your for loop is done long before that.
When you use await, you are blocking following code, until the promise resolves. This means that each call to tokenOfOwnerByIndex has to resolve, before the for loop continues.
See my code for an example.
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
console.time('promise');
let c = 10;
for (let i = 0; i < 10; i++) {
sleep(100)
.then(() => {
c--;
if (c === 0) {
console.timeEnd('promise');
}
});
}
console.time('await');
(async () => {
let c = 10;
for (let i = 0; i < 10; i++) {
await sleep(100);
c--;
if (c === 0) {
console.timeEnd('await');
}
}
})();

Equivelance between two different dynamic promise provider chains

I am wondering if the two code blocks are basically equivalent:
const executePromises = function(listOfProviders){
let 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]();
}
};
does anyone see a discrepancy?
No. They are not the same. Even if you return the promise as someone pointed out in comments.
Difference:
When using async await with for-loop, it should be remembered that for loop goes to next iteration only when the awaited promise has been resolved in the current iteration.
On the other hand, when .then is used in a for-loop, that for-loop doesn't wait for the promise to resolve before going to the next iteration.
I have tweaked your code to demonstrate this. Observe that Line A is being called at different point of time in each code snippet.
Async await inside for-loop code:
function createPromiseWhichResolveAfterOneSec(i) {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
console.log("About to resolve promise with value:",i);//Line A
resolve(i);
},1000)
})
}
const executePromises = async function(listOfProviders) {
let p;
let i;
for(i = 0; i < listOfProviders.length; i++){
console.log("before", i);
p = await createPromiseWhichResolveAfterOneSec(i);
console.log("after", i);
}
console.log(i);
return p;
};
let listOfProviders = [1,2,3,4,5];
let promise = executePromises(listOfProviders);
promise.then(value => console.log("this is the final value of promise chain:",value)).catch(()=>{});
With then in for-loop:
function createPromiseWhichResolveAfterOneSec(i) {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
console.log("About to resolve promise with value:",i);//Line A
resolve(i);
},1000)
})
}
const executePromises = function(listOfProviders){
let p = Promise.resolve(null);
for(let i = 0; i < listOfProviders.length; i++){
console.log("before", i);
p = p.then(v => createPromiseWhichResolveAfterOneSec(i));
console.log("after", i);
}
return p;
};
let listOfProviders = [1,2,3,4,5];
let promise = executePromises(listOfProviders);
promise.then(value => console.log("this is the final value of promise chain:",value)).catch(()=>{});

How to handle multiple arrays in asynchronous methods in node.js

I have a very large array (10K) and i want to split it (i did according to this: https://stackoverflow.com/a/8495740/2183053 and it worked) but i need to pass the tempArray to request and wait for response which will be passed to savetodb.
Can someone help me out please. To be clear, I want to split the large Array and pass the separated arrays to request function then pass to save to db and continue this process until all the arrays are cleared.
the following is the code i did:
//i used waterfall because it worked in waiting to finish the first task before starting another
async.waterfall([
async.apply(handleFile, './jsonFile.json')
runRequest1,
savetoDb
], function(err) {
console.log('waterfall1 complete')
})
function handleFile(data, callback) {
var name
var authorNames = []
require('fs').readFile(data, 'utf8', function(err, data) {
if (err) throw err
var content = _.get(JSON.parse(data), [2, 'data'])
for (var x = 0; x < content.length; x++) {
authorNames.push(JSON.stringify(content[x]['author']))
}
//the large array is authorNames and im splitting it below:
for (i = 0, j = authorNames.length; i < j; i += chunk) {
temparray = authorNames.slice(i, i + chunk);
setTimeout(function() {
callback(null, temparray)
}, 2000);
}
})
}
You need promise to handle async things in nodejs
let findAllReceipts = function (accountArray) {
const a = [];
for (let i = 0; i < accountArray.length; i++) {
a.push(new Promise((resolve, reject) => {
receiptTable.find({accountNo: accountArray[i].accountNo}, function (err, data) {
if (!err) {
resolve(data);
} else {
reject(new Error('findPrice ERROR : ' + err));
}
});
}));
}
return Promise.all(a);
};
I added some promise.
const data = await handleFile('./jsonFile.json');
// save to db
async function handleFile(filePath) {
let arrayWillReturn = [];
var name
var authorNames = []
let data = await getFileData(filePath)
var content = _.get(JSON.parse(data), [2, 'data'])
for (var x = 0; x < content.length; x++) {
authorNames.push(JSON.stringify(content[x]['author']))
}
//the large array is authorNames and im splitting it below:
for (i = 0, j = authorNames.length; i < j; i += chunk) {
arrayWillReturn.push(authorNames.slice(i, i + chunk));
}
return arrayWillReturn;
}
async function getFileData(fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, type, (err, data) => {
err ? reject(err) : resolve(data);
});
});
}
I'm answering my own question for future references. the only thing that I added to make my code work is Promise. It sounds easy but it took me a while to grasp the function and implementing it but it worked and it was worth it. Thank you so much for the responses.

Nested promise in .then running before 1st promise node

I'm having a problem with my program at the moment in which I am running a promise in the .then of a promise, however it is running the 2nd promise before the first. This is not what I want since it relies on the 1st promise to work. Here is my code (The console.logs are random, but I just came up with stuff on the spot for checking when things run)
new Promise((resolve, reject) => {
for (i = 0; i < 10; i++) {
var query = {
class: result[`class${i}`]
};
dbo.collection("homeworks").find(query).toArray(function(err, result) {
if (err) throw err;
for (i = 0; i < result.length; i++) {
current = current + 1;
console.log(current)
allhomework[current] = result[i];
console.log(allhomework[current])
}
db.close();
});
}
console.log("HOLA");
resolve(allhomework);
})
.then((allhomework) => {
console.log("HOLA MIS AMIGOS")
new Promise((resolve, reject) => {
console.log("v");
for (i = 0; i < 10; i++) {
console.log("uaefshiudvhofdh")
console.log(allhomework[1]);
}
resolve();
}).then(() => {
response.render('index', {
username: req.session.user,
homeworks: allhomework
});
})
})
.catch(console.log)
Well, the best way to solve this would be to use the promise interface for your database and use Promise.all() to track when all the async database operations in your loop are done. But, if you want to code it manually with your existing loop, you just have to keep a counter for when they are all done.
new Promise((resolve, reject) => {
let cntr = 10;
for (let i = 0; i < 10; i++) {
var query = {
class: result[`class${i}`]
};
dbo.collection("homeworks").find(query).toArray(function(err, result) {
if (err) {
db.close();
return reject(err);
}
for (let i = 0; i < result.length; i++) {
current = current + 1;
console.log(current)
allhomework[current] = result[i];
console.log(allhomework[current])
}
--cntr;
if (cntr === 0) {
db.close();
resolve(allHomework);
}
});
}
console.log("HOLA");
});
Other notes about this code:
I'm also wondering why there's a db.close() in the middle of your outer loop. I would think that would cause a problem.
I also added a reject(err) to your if (err) check.
You have loop conflict with your i variable. Put a let for each one to make them separate or change one of them to a different letter.
Here's a version that uses the promise interface in your database. I was able to completely get rid of the inner loop and just let promises collect all the results in order for us. Monbodb's .toArray() returns a promise that resolves with the query result if you don't pass it a callback. We can use that with Promise.all() to collect all the results in order and to tell us when they are all done:
let promises = [];
for (let i = 0; i < 10; i++) {
let query = {
class: result[`class${i}`]
};
promises.push(dbo.collection("homeworks").find(query).toArray());
}
Promise.all(promises).then(allResults => {
// flatten the results into all one array - promises will have kept them in proper order
db.close();
let results = [].concat(...allResults);
// now do whatever you want with the final array of results
}).catch(err => {
db.close();
// handle error here
});

javascript - have promises call at end of for loop

I have this code and I want to make the code wait until the asyncrnous query called prom has finessed before restarting the first for loop. So the array will be reset, before it starts the first for loop again.
items = [];
var promises = [];
for (var i = 0; i < userArray.length; i++) {
items.length = 0;
for (var i2 = 0; i2 < test.length; i2++) {
var UserFavourite = Parse.Object.extend("UserFavourite");
var queryUserFav = new Parse.Query(UserFavourite);
queryUserFav.equalTo('item', test[i2].get('item'));
queryUserFav.equalTo('school', test[i2].get('school'));
queryUserFav.equalTo('user1', userArray[i])
var prom = queryUserFav.find().then(function(res) {
for (var i3 = 0; i3 < res.length; i3++){
var item = res[i3];
var itemName = item.get('item');
items.push(itemName);
console.log(items)
}
return items;
});
promises.push(prom);
}
//return Parse.Promise.when.apply(Parse.Promise, promises); I have tried it here but
// this just stops the first for loop after its first loop
}
return Parse.Promise.when.apply(Parse.Promise, promises);
What you're trying to do is have a chain of promises, one for each item in an array.
If would be nice if javascript had an equivalent of .NET's await keyword, where you could go
await Parse.Promise.when(promises)
and then it allowed the promise code to run then returned back to running any code after the await. But Javascript doesn't give this to us.
Another approach is to maintain an index variable. After every set of queries is processed, you increment the index variable and process the next set of values.
function parseForUser(user) {
var promises = [];
for (var i2 = 0; i2 < test.length; i2++) {
var items = [];
var UserFavourite = Parse.Object.extend("UserFavourite");
var queryUserFav = new Parse.Query(UserFavourite);
queryUserFav.equalTo('item', test[i2].get('item'));
queryUserFav.equalTo('school', test[i2].get('school'));
queryUserFav.equalTo('user1', user)
var prom = queryUserFav.find().then(function(res) {
for (var i3 = 0; i3 < res.length; i3++){
var item = res[i3];
var itemName = item.get('item');
items.push(itemName);
console.log(items)
}
return items;
});
promises.push(prom);
}
return Parse.Promise.when(promises);
}
function parseUserArray(userArray) {
var returnPromise = new Parse.Promise(); // Do you have to call it via new?
//The documentation isn't clear.
var index = 0;
var doNext = function() {
if(index < userArray.length) {
var promise = parseForUser(userArray[index++]);
promise.done(doNext);
} else {
returnPromise.resolve();
}
}
doNext();
return returnPromise;
}
var parseUserArrayPromise = parseUserArray(userArray);
FWIW ...
This solution differs from #AndrewShepherd's chiefly in that here we take double advantage of the promise returned by asyncProcessUser().
firstly for flow control - the asynchronous sequencing of the inner loop
secondly for delivering an array of results, from which the final array of results is built, avoiding the need for an outer promises array.
function parseUserArray(userArray, test) {
// This function is the original inner for() loop, now expressed as a .map(),
// (plus peripheral wrapping and chaining).
// It asynchronously processes a single user against all test items,
// and returns a promise of an array of results.
// The promise resolves when all the individual .finds complete.
function asyncProcessUser(user) {
return Parse.Promise.when(test.map(function(dataItem) {
return (new Parse.Query(Parse.Object.extend("UserFavourite")))
.equalTo('item', dataItem.get('item'))
.equalTo('school', dataItem.get('school'))
.equalTo('user1', user)
.find().then(function(res) {
return res.map(function(r) {
return r.get('item');
});
});
})).then(function() {
return Array.prototype.slice.apply(arguments).reduce(function(arr, arr_) {
return arr.concat(arr_);
}, []);
});
}
// This is the original outer for() loop, now expressed as a .reduce(), which is
// a common pattern for performing a series of async tasks (ie what was the inner loop).
// Here, `userArray.reduce(...)` returns a promise of an array of
// the objects returned by `r.get('item')` above.
return userArray.reduce( function(p, user) {
return p.then(function(arr) {
return asyncProcessUser(user).then(function(arr_) {
return arr.concat(arr_);
});
});
}, Parse.Promise.as([]) );//† "returns a new promise that is resolved with a given value".
}
† : Documentation for Parse.Promise.as()
Without the comments, it's quite concise.
The concept is demonstrated here. Don't worry that the demo uses jQuery promises, it's the concept that matters.
Use this
function parseForUser(user) {
var promises = [];
for (var i2 = 0; i2 < test.length; i2++) {
var items = [];
var UserFavourite = Parse.Object.extend("UserFavourite");
var queryUserFav = new Parse.Query(UserFavourite);
queryUserFav.equalTo('item', test[i2].get('item'));
queryUserFav.equalTo('school', test[i2].get('school'));
queryUserFav.equalTo('user1', user)
var prom = queryUserFav.find().then(function(res) {
for (var i3 = 0; i3 < res.length; i3++){
var item = res[i3];
var itemName = item.get('item');
items.push(itemName);
console.log(items)
}
return items;
});
promises.push(prom);
}
return Parse.Promise.when(promises);
}
function parseUserArray(userArray) {
var returnPromise = new Parse.Promise(); // Do you have to call it via new?
//The documentation isn't clear.
var index = 0;
var doNext = function() {
if(index < userArray.length) {
var promise = parseForUser(userArray[index++]);
promise.done(doNext);
} else {
returnPromise.resolve();
}
}
doNext();
return returnPromise;
}
Just copy and paste

Categories