i need access all axios data after for loop - node.js

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.

Related

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

"Failed to pipe. The response has been emitted already" when reading a stream (nodejs)

So my code is supposed to read some lines from a CSV file, convert them to an array of JSON objects, and return that array.
To read the file as a stream, I am using got, and then using it in fast-csv.
In order to return the resulting array, I put the entire thing into a Promise like this:
async GetPage() : Promise<{OutputArray:any[], StartingIndex:number}>{
return new Promise(async (resolve, reject) => {
const output:any[] = [];
const startingIndex = this.currentLocation;
try{
parseStream(this.source, {headers:true, maxRows:this.maxArrayLength, skipRows:this.currentLocation, ignoreEmpty:true, delimiter:this.delimiter})
.on('error', error => console.log(`parseStream: ${error}`))
.on('data', row => {
const obj = this.unflatten(row); // data is flattened JSON, need to unflatten it
output.push(obj); // append to output array
this.currentLocation++;
})
.on('end', (rowCount: number) => {
console.log(`Parsed ${this.currentLocation} rows`);
resolve({OutputArray:output, StartingIndex:startingIndex});
});
}
catch(ex){
console.log(`parseStream: ${ex}`);
throw new Error(ex);
}
})
}
Now when I call this once (await GetPage()) it works perfectly fine.
The problem is when I'm calling it a second time in a row. I'm getting the following:
UnhandledPromiseRejectionWarning: Error: Failed to pipe. The response has been emitted already.
I've seen a similar case over here: https://github.com/sindresorhus/file-type/issues/342 but from what I gather this is a different case, or rather if it's the same I don't know how to apply the solution here.
The GetPage is a method inside a class CSVStreamParser which is given a Readable in the constructor, and I create that Readable like this: readable:Readable = got.stream(url)
What confuses me is that my first version of GetPage did not include a Promise, but rather accepted a callback (I just sent console.log to test it) and when I called it several times in a row there was no error, but it could not return a value so I converted it to a Promise.
Thank you! :)
EDIT: I have managed to make it work by re-opening the stream at the start of GetPage(), but I am wondering if there is a way to achieve the same result without having to do so? Is there a way to keep the stream open?
First, remove both of the async, since you are already returning a Promise.
Then remove the try/catch block and throw since you shouldn't throw in a promise. Instead use the reject function.
GetPage() : Promise<{OutputArray:any[], StartingIndex:number}>{
return new Promise((resolve, reject) => {
const output:any[] = [];
const startingIndex = this.currentLocation;
parseStream(this.source, {headers:true, maxRows:this.maxArrayLength, skipRows:this.currentLocation, ignoreEmpty:true, delimiter:this.delimiter})
.on('error', error => reject(error))
.on('data', row => {
const obj = this.unflatten(row); // data is flattened JSON, need to unflatten it
output.push(obj); // append to output array
this.currentLocation++;
})
.on('end', (rowCount: number) => {
console.log(`Parsed ${this.currentLocation} rows`);
resolve({OutputArray:output, StartingIndex:startingIndex});
});
});
}
Here's some resources to help you learn about async functions and promises.

What is a good design pattern for awaiting Promise.all with extra data

I would like some guidance on a good way to pass data along with an array of promises so that the data can be used later after calling await Promise.all().
I'm pretty new to node and promises. The code below, in summary, does what I want, but it seems messy to have the promises array separate from the corresponding data array. I would think this is a common occurrence and would have a simpler solution.
I was able to accomplish the same result by attaching a then() function to the asyncFunc return, but this makes the code messier and for some reason takes twice as long to execute.
async function foo(inputs) {
var promises = [];
var datas = [];
for (var input of inputs.values()) {
promises.push(asyncFunc(input.a));
datas.push(input.b);
}
var outputs = [];
for (let result of (await Promise.all(promises)).values()) {
outputs.push({
data: datas.shift(),
result: result
});
}
return outputs;
}
You will often face this kind of problem in nodeJs, the answer is how you want your async function react.
If you want it run sequentially, you don't need promise all. The code will look like this.
async function foo(inputs) {
const results = [];
const datas = [];
for (const input of inputs.values()) {
const result = await asyncFunc(input.a);
results.push(result);
datas.push(input.b);
}
const outputs = [];
for (const result of results) {
outputs.push({
data: datas.shift(),
result: result
});
}
return outputs;
}
In case you have to use the promise all, you can use another variable to hold the values, this will make the code less messy.
const results = await Promise.all(promises)).values();
for (const result of results) {
outputs.push({
data: datas.shift(),
result: result
});
}

Redis Node - Get from hash - Not inserting into array

My goal is to insert the values gotten from a redis hash. I am using the redis package for node js.
My code is the following:
getFromHash(ids) {
const resultArray = [];
ids.forEach((id) => {
common.redisMaster.hget('mykey', id, (err, res) => {
resultArray.push(res);
});
});
console.log(resultArray);
},
The array logged at the end of the function is empty and res is not empty. What could i do to fill this array please ?
You need to use some control flow, either the async library or Promises (as described in reds docs)
Put your console.log inside the callback when the results return from the redis call. Then you will see more print out. Use one of the control flow patterns for your .forEach as well, as that is currently synchronous.
If you modify your code to something like this, it will work nicely:
var getFromHash = function getFromHash(ids) {
const resultArray = [];
ids.forEach((id) => {
common.redisMaster.hget('mykey', id, (err, res) => {
resultArray.push(res);
if (resultArray.length === ids.length) {
// All done.
console.log('getFromHash complete: ', resultArray);
}
});
});
};
In your original code you're printing the result array before any of the hget calls have returned.
Another approach will be to create an array of promises and then do a Promise.all on it.
You'll see this kind of behavior a lot with Node, remember it uses asynchronous calls for almost all i/o. When you're coming from a language where most function calls are synchronous you get tripped up by this kind of problem a lot!

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);
});
});

Resources