execute commands sequentially in Promose/bluebird - node.js

I have an array that need to append to a database, the condition is, element has to be appended one after another to make it work, following is my code, seems commands are not executed sequentially, what's wrong with my code:thanks.
var B = require('bluebird')
var appends = []
recs.forEach(function (item) {
appends.push(dao.append_rec_cartAsync(item))
})
B.all(appends).then(function () {
console.log('all done')
})

When you call dao.append_rec_cartAsync(item) you're executing the asynchronous operation. Once it's started you can't really do anything about it running. Also note that Promise.all does not do things sequentially but rather parallely.
var B = require('bluebird');
B.each(recs, function(item){ // note we use B.each and not forEach here
// we return the promise so it knows it's done
return dao.append_recs_cartAsync(item);
}).then(function(){
console.log("all done!")
});
Or in short: B.each(recs, dao.append_recs_cartAsync)

Related

i need access all axios data after for loop

I'm making a simple word combinatiion website.
and as a final step, I need all possible word in one string
so I write code like this
const fs=require('fs');
const axios=require('axios')
function test(want){
const res=axios.get("http://api.dictionaryapi.dev/api/v2/entries/en/"+want);
const datapromise=res.then((res)=>res.data);
return datapromise
}
fs.readFile('./input.txt','utf-8',function(error,data){
//console.log("console log")
var array=data.toString().split("\n");
fs.writeFile("./log.txt","",(err)=>{});
var res=""
for(i in array){
test(array[i]).then((data)=>(data)=>res+=data[0].word+"<br>").catch(/*(data)=>console.log(data.code)*/);
}
console.log(res);
})
But this code isn't work. console.log(res); is executed first and followed by for loop.
How can I fix it?
Without knowing much about Axios I can tell that axios.get and therefore the test function is going to be async. This means console.log here will always run first here as a result. Test ends up returning a promise that will resolve at a later time.
I'd do something like this (assuming you don't have async/await available):
var res= "";
var promises = [];
for(i in array) {
promises.push(
test(array[i]).then((data) => res+=data[0].word + "<br>")
);
}
Promise.all(promises).finally(() => {
console.log(res);
});
Other notes:
The catch here is being called but nothing is being passed in - this may result in an error
The then has a nested function that I imagine wouldn't ever be called (data) => (data) => this is basically creating a 2nd nested function. I don't think it'd get called.

Node js issue with loop order

Hello i really need help with this issue, my last console.log is execute BEFORE the for loop and i dont know how to fix it. I really need to have access at my array nbfilm after the for loop
Can someone help me?
What the console print : lien
client.db.query("SELECT name,id FROM film", function (err, result) {
if (err) throw err;
const catalog = new MessageEmbed()
.setTitle("Catalogue")
.setColor("#fcfe80")
.setFooter({text:"🍿 ・ PopFlix"})
let testresult =[]
let nbfilm =[]
for (let compteur of result){
testresult.push(compteur.id)
testresult.push(compteur.name)
}
console.log(testresult)
for (let compteur2 = 0; compteur2 < testresult.length; compteur2+=2){
client.db.query(`SELECT link FROM lien WHERE fid=${testresult[compteur2]}`, function (err,result) {
nbfilm.push(testresult[compteur2+1])
nbfilm.push(result.length)
console.log("nbfilm in for loop",nbfilm)
});
}
console.log("nbfilmAFTER",nbfilm)
});
The body of the loop delays execution. Due to the fact that javascript is an asynchronous i/o type operation language, it is common to return a promise. In other words, the code is executed as expected, but the result of the actions will be visible only after all pending tasks have been completed. In your case, adding async/await in code might help. Use it node docs
It looks like client.db.query is asynchroneous. JS here works as expected because it doesnt wait for the query to be finished before moving to the next line.
Its now a better practice to use async/await instead of callbacks. If you provide the package that you are using we can provide a code example.
client.db.query() is an asynchronous function and won't execute until after the console.log(nbfilm) is executed regardless of how fast that query actually runs.
I'd recommend using Promise.all(). You will also have to "promisify" the query() function. Pass everything you want to resolve(), and then concatenate them in the final line as below.
let promises = [];
for(....) {
promises.push(new Promise((resolve, reject) => {
client.db.query("select ... ", () => {
// all the same stuffs
resolve([testresult[compteru2+1], result.length]);
});
}))
}
Promise.all(promises)
.then((results) => results.reduce((p, c) => p.concat(c), []))
.then(data => console.log(data)); // do whatever you want with "data"
Here's a simplified demo:
https://codesandbox.io/s/promise-all-example-78r347

About the local variable in Node.js promise

I am a newer of Node.js.I defined a array as a local variable,and want to use it in the following then,I save some useful data in it.But in the end, the array is empty.Can somebody tell me why?Thanks for your support.
const Device = require("./mongo.js").Device;
const Video = require("./mongo.js").Video;
Device.findOne({id:"11112222"}).exec()
.then(function(data){
var videoIds = data.videoIds.split(",");
var videoId2URL = [];
console.log(videoIds);
videoIds.forEach(function(one){
return Video.findOne({id:one}).exec()
.then(function(data){
videoId2URL.push({id:one,url:data.url});
return videoId2URL;
})
});
console.log(videoId2URL);
});
The problem is that you are displaying videoId2URL too early.
Device.findOne returns a promise executed asynchronously. But Video.findOne also returns a promise executed asynchronously.
So when you do console.log(videoId2URL);, the promises created by Video.findOne are not executed yet. So your array is empty.
You must wait the end of all your promises. You can use Promise.all for that.
Promise.all(videoIds.map(function(one){
return Video.findOne({id:one}).exec()
.then(function(data){
videoId2URL.push({id:one,url:data.url});
return videoId2URL;
});
})
.then(function() {
console.log(videoId2URL);
});
You could use Promise.all to resolve your problem. You forEach code contains async code. Your last line does not wait for all promises to get resolved.
Try with:
var arr = [];
videoIds.forEach(function(one){
return arr.push(Video.findOne({id:one}).exec());
});
Promise.all(arr) // here we are waiting for all async tasks to get resolved
.then(function(data){
console.log(data);
// parse your data here and find array of videoId2URL
})
When you do console.log(videoId2URL), you're still in the main stack for the script, while none of the push callbacks have been executed.
You can use an array to collect the promises returned by Video.findOne, and at the end use Promise.all to drain all the promises and do the log then.
BTW, none of the 2 return are necessary, you can safely remove them.
The 1st one is not used because it's in a synchronous callback for forEach.
The 2nd one is not used because you're relying on the side effect, rather than use the resolved value.
Try:
const Device = require("./mongo.js").Device;
const Video = require("./mongo.js").Video;
Device.findOne({id:"11112222"}).exec()
.then(function(data){
var videoIds = data.videoIds.split(",");
var videoId2URL = [];
var promiseArr = [];
console.log(videoIds);
videoIds.forEach(function(one){
var p = Video.findOne({id:one}).exec()
.then(function(data){
videoId2URL.push({id:one,url:data.url});
});
promiseArr.push(p);
});
Promise.all(promiseArr).then(function() {
console.log(videoId2URL);
});
});

Wait for the nested loops to finish in nodejs

I am very new to node.js and here is my problem
I have a nested set of maps here(I have tried using for-each as well).
All the "get..." methods nested are returning a promise defined by me. I have to loop over the data returned by the methods continuously and use it to call the next "get.." method(there is a dependency). After completing the entire nested loops, the "allobj" array should have my final result and I am trying to return it once all the looping is done. I have not had any luck so far, as an empty array gets returned.
var allobj = new Array();
var catcount = 0;
//categories.forEach(function(category){
var p = categories.map(function(category,index,array){
catcount++;
return gettests(runid,category).then(function(tests){
//console.log(tests.sort().reverse()); //this is sync
//tests.forEach(function(test){
var t = tests.map(function(test,index,array){
return getmetriccategories(runid,category,test).then(function(data){
//console.log(data);
//data.forEach(function(mc){
var d = data.map(function(mc,index,array){
return getmetrics(runid,category,test,mc).then(function(data1){
var x = data1.map(function(metric,index,array){
allobj.push(metric);
return metric;
});
});
});
})
})
})
})
//return when all nested loops are done
res.send(allobj);
Your functions getmetriccategories and getmetrics return Promises, at least, their return values seem to have a then method which is an indicator for that. This means, they work asynchronously. So, the map calls don't return the results directly, but an array of Promises. To wait for all of these Promises to be fulfilled (which means, the asynchronous functions are completed), you can use Promise.all function:
Promise.all(data.map(...))
.then(function (result) {
res.send(result)
})
As you see, Promise.all returns a new Promise, so you can use then to receive the result and send it with res.send.

Execute when both(!) events fire .on('end')

I have a node app that reads two files as streams. I use event.on('end') to then work with the results. The problem is I don't really know how I can wait for BOTH events to trigger 'end'.
What I have now is:
reader1.on('end', function(){
reader2.on('end',function(){
doSomething();
});
});
With small files this works, but if one of the files is very large the app aborts.
Your execution logic is somewhat flawed. You ought to do something like this instead
var checklist = [];
// checklist will contain sort of a counter
function reader_end(){
if(checklist.length == 2 )
// doSomething only if both have been added to the checklist
doSomething();
}
reader1.on('end', function() {
checklist.push('reader1');
// increment the counter
reader_end();
});
reader2.on('end', function() {
checklist.push('reader2');
reader_end();
});
Although there are libraries to better handle this sort of stuff, like Async and Promises.
With Async you'll need to use compose
var r12_done = async.compose(reader1.on, reader2.on);
r12_done('end', function(){
doSomething();
});
Edit: I just noticed that since probably reader1.on is a Stream 'end' event which doesn't have the standard callback argument signature of (err, results), this probably won't work. In that case you should just go with Promise.
With Promise you'll need to first Promisify and then join
var reader1Promise = Promise.promisify(reader1.on)('end');
var reader2Promise = Promise.promisify(reader2.on)('end');
var reader12Promise = Promise.join(reader1Promise, reader1Promise);
reader12Promise.then(function(){
doSomething();
});

Resources