Maintain Order In Async Function Node JS - javascript

I am new to NodeJs.
I want to push elements in files array in order of url. But i am getting random order .
Below is the code for same. Can anyone suggest what i am doing wrong here.
const mergePdf =async (urlArray, files) => {
for (let i = 0; i < urlArray.length; i++) {
try {
const pdfBytes = await fetch(urlArray[i]).then((res) => {
return res.arrayBuffer();
});
let bytes = new Uint8Array(pdfBytes);
files[i]=bytes;
} catch (err) {
console.log(err);
}
}
}

write seprate function of your api call in promises like
const other_func = (url) => {
return new Promise(async (resolve) => {//updated line
try {
const pdfBytes = await fetch(url).then((res) => {
let bytes = new Uint8Array(pdfBytes);
resolve(bytes);// return value pass in resolve method [Updated]
});
} catch (err) {
console.log(err);
}
})
}
and in your original function call this function using await
like
const mergePdf =async (urlArray, files) => {
for (let i = 0; i < urlArray.length; i++) {
files[i] = await other_func(urlArray[i])
}}

Related

How to read files present in array nodejs

I would like to know to read the files and search for keyword sample in nodejs.
If keyword found, display the path
const allfiles = [
'C:\\Users\\public',
'C:\\Users\\public\\images',
'C:\\Users\\public\\javascripts\\index1.js',
'C:\\Users\\public\\javascripts\\index2.js'
]
const readFile = (path, opts = 'utf8') =>
new Promise((resolve, reject) => {
try{
let result=[];
fs.readFile(path, opts, (err, data) => {
if (err) reject(err)
else {
if(data.indexOf("sample")>=0){
result.push(data);
resolve(result);
}
}
})
}
catch (e) {
console.log("e", e);
}
})
const run = async () => {
allfiles.forEach(e=>{
const s = await readFile(e);
console.log(s);
})
}
run();
Expected Output
[
'C:\\Users\\public\\javascripts\\index1.js',
'C:\\Users\\public\\javascripts\\index2.js'
]
Some tips:
What happens when "sample" isn't found in readFile?
You're currently pushing the data into result instead of the path.
Think about what you're trying to accomplish with readFile. To me, what you want to do is see if that file has the word "sample", and return true if so and if not return false. So I'd name the function checkIfFileHasSample and have it return a boolean. Then in your run function, in the forEach you have the path, so that is where I'd add the path to a list of results.
Maybe you already realized this, but run is never actually called in your code sample. Ie. run() doesn't happen.
Solution:
You had some syntax errors and a tricky gotcha with async-await with run. For the syntax errors, it'll come with experience, but I'd also recommend using ESLint to help you catch them, as well as making sure your code is always properly indented.
const fs = require("fs");
const allfiles = [
"C:\\Users\\public",
"C:\\Users\\public\\images",
"C:\\Users\\public\\javascripts\\index1.js",
"C:\\Users\\public\\javascripts\\index2.js",
];
const checkIfFileHasSample = (path, opts = "utf8") =>
new Promise((resolve, reject) => {
fs.readFile(path, opts, (err, data) => {
if (err) {
reject(err);
} else {
if (data.includes("sample")) {
resolve(true);
} else {
resolve(false);
}
}
});
});
const run = async () => {
const results = [];
for (let i = 0; i < allFiles.length; i++) {
try {
const file = allFiles[i];
const hasSample = await checkIfFileHasSample(file);
if (hasSample) {
results.push(file);
}
} catch (e) {
console.log(e);
}
}
console.log(results);
};
run();

writeFile does not wait for variable to be instantiated

I'm new to node.js and javascript in general but I am having issues understanding why the writeFile function is writing a blank file. I think the for loop should be a Promise but I am not sure how to write it.
const removeProviders = function () {
readline.question('Where is the file located? ', function(filePath) {
let providerArray = fs.readFileSync(filePath).toString().split('\r\n');
console.log(providerArray);
let importFile = '';
for (let providerId in providerArray) {
getProvider(providerArray[providerId]).then((response) => {
let providerInfo = response.data;
return providerInfo;
}).then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += "entry";
})
}
fs.writeFile('C:/path/to/file.txt', importFile);
You can collect all the promises from the for-loop into an Array and then pass them into Promise.all() which will resolve only after all of them resolved. If one of the promises are rejected, it will reject as well:
const promises = providerArray.map((item) => {
return getProvider(item)
.then((response) => {
let providerInfo = response.data;
return providerInfo;
})
.then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += "entry";
});
});
Promise.all(promises).then(() => {
fs.writeFile('C:/path/to/file.txt', importFile, err => {
if (err) {
console.error(err);
}
});
});
While doing this you could also get rid of the importFile variable and collect directly the results of your promises. Promise.all().then(results => {}) will then give you an array of all results. Thus no need for an updatable variable.
The below approach may be useful.
I don't know your getProvider return Promise. you can set promise in getProvider method
const getProviderValue = async function(providerArray) {
return new Promise((resolve, reject) {
let importFile = '';
for (let providerId in providerArray) {
await getProvider(providerArray[providerId]).then((response) => {
let providerInfo = response.data;
return providerInfo;
}).then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += "entry";
})
}
resolve(importFile)
})
}
const removeProviders = async function () {
readline.question('Where is the file located? ', function(filePath) {
let providerArray = fs.readFileSync(filePath).toString().split('\r\n');
console.log(providerArray);
let importFile = await getProviderValue(providerArray)
fs.writeFile('C:/path/to/file.txt', importFile);
})
}
Your for loop does not wait for the promises to resolve. A better way to approach this problem would be to use reduce.
providerArray.reduce(
(p, _, i) => {
p.then(_ => new Promise(resolve =>
getProvider(providerArray[providerId]).then((response) => {
let providerInfo = response.data;
return providerInfo;
}).then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += entry;
resolve();
}))
);
}
, Promise.resolve() );
You can also use Promise.all but the out data may not be in the order that you expect if you append to the importFile variable.

Nested promises with for loop doesn't work

I have this nested loop of promises and at then end a for loop that pushes items in the files array.
public async content() {
let files = [];
return new Promise(async (resolve, reject) => {
await this.axios.get(this.currentRequest).then(async biosample => {
await this.axios.get(this.currentRequest + biosample.data.item).then(async datasets => {
for (let i = 0; i < datasets.data.Items.length; i++) {
await this.axios.get(this.currentRequest + datasets.data.Items[i].Id).then(response => {
files.push(response.data.Item);
}).catch(reason => {
reject(reason)
});
}
})
}).catch(function (error) {
reject(new Error(error.response))
});
resolve(files)
})
}
The calls are made correctly, because if I use Promise.all([promises here]), then it works. But I'm trying to learn to chain promises properly.
When I'm debugging with webstorm datasets seems to be defined and have the necessary properties.
Schematically you code must like this
content() {
return Promise.resolve()
.then(_ => this.axios.get(this.currentRequest)
.then(biosample => this.axios.get(this.currentRequest + biosample.data.item))
.then(datasets => Promise.all(datasets.data.Items.map(item => this.axios.get(this.currentRequest + item.Id))))
}
You're not using the potential of await. Your code can be as simple at this:
public async content() {
let files = [];
return new Promise(async (resolve, reject) => {
try {
let biosample = await this.axios.get(this.currentRequest)
let datasets = await this.axios.get(this.currentRequest + biosample.data.item)
for (let i = 0; i < datasets.data.Items.length; i++) {
let response = await this.axios.get(this.currentRequest + datasets.data.Items[i].Id)
files.push(response.data.Item);
}
resolve(files)
} catch(error) {
reject(new Error(error.response || error))
};
})
}

How to recover a result of query mongodb outside of its function?

I am a new javascript user and I am looking for a rescuer the result (count) of a memory request.
More precisely, maybe you can comment, put my variable in my conv object
right now my result is [promised object]
I tried with async and promised javascript but I do not understand the logic yet
Thank you so much,
Gabriel
var conversations = []
for (var i = 0; i < conversation.length; i++) {
for (var j = 0; j < conversation[i].users.length; j++) {
conv = conversation[i].users[j]
async function asyncCall() {
function countUnread() {
filterUnread = {$and:[
{toID: user.properties.chat.id},
{fromID: conversation[i].users[j].id},
{ read: false }]}
return new Promise(resolve => {
Message.countDocuments(filterUnread, function (err, count) {
resolve(count)
})
})
}
var count = await countUnread();
console.log(count);
console.log(conv)
resolve(!!!!count!!!!) ;
}
asyncCall();
conv.unread = !!!!I_want_my_count_here!!!!
conversations.push(conv);
resolve(conversations)
}
}
Mixing async functions and Promise constructors makes the code hard to track and causes problems like this one. Instead only wrap the most low level parts into Promise constructors, let tuem resolve to something useful, then compose multiple such Promises using async :
const filter = { users: { $elemMatch: { id: user.properties.chat.id } } }
function getConversations(filter) {
return new Promise(function(resolve, reject) {
Conversation.find(filter, function(err, conversations) {
if(err) reject(err) else resolve(conversations);
});
});
}
function countUnread(user) {
const filterUnread = { $and:[
{toID: user.properties.chat.id},
{fromID: user.id},
{ read: false }
]};
return new Promise((resolve, reject) => {
Message.countDocuments(filterUnread, function (err, count) {
if(err) reject(err) else resolve(count);
});
});
}
async function composeConversations() {
const conversations = await getConversations();
const result = [];
for(const conversation of conversations) {
for(const user of conversation.users) {
const count = await countUnread(user);
user.unread = count;
}
result.push(conversation);
}
return result;
}

Promise all with inner function to be executed

I want to retrieve different HTML body at once and as soon as all results are available work with that content.
My callback solution which works looks like this (probably only relevant to read if the idea is not clear, otherwise skip ;)):
const request = require('request')
const argLength = process.argv.length
const result_array = []
let counter = 0
function executeRequest () {
for (start = 2; start <= argLength - 1; start++) {
const copy = start
function callback (error, res, body) {
const startCopy = copy
if (error) {
console.log('error')
return
}
result_array[startCopy - 2] = body.toString().length
counter++
if (counter === argLength - 2) {
console.log(result_array)
}
}
request(process.argv[start], callback)
}
}
executeRequest()
Now I try to make it running with Promises like this:
const httpRequest = require('request')
const argumentLength = process.argv.length
function fillURLArray () {
resultArray = []
for (start = 2; start < argumentLength; start++) {
resultArray[start - 2] = process.argv[start]
}
return resultArray
}
const urls = fillURLArray()
let counter = 0
function readHttp () {
const resultArray = []
Promise.all(urls.map(url => httpRequest(url, (error, res, body) => {
console.log(body.toString())
resultArray[counter++] = body.toString()
}))).then(value => {
console.log('promise counter: ' + counter++)
console.log(resultArray)
console.log('called')
})
}
readHttp()
I tried already several attempts with different promise chains but every time I get either not a result or just an empty array. So obviously the .then() function is called before the array is actually filled (at least I guess so since console.log(body.toString()) appears to print the content some time later)
Any idea how to solve this with promises?
Thank you
request is not returning promise object so have created a method that return promise object on which you do Promise.all.
function requestPromise(url){
return new Promise((resovle,reject) => {
httpRequest(url, (error, res, body) => {
if(err){
reject(err);
}
resolve(body.toString());
});
});
}
function readHttp () {
const resultArray = []
Promise.all(urls.map(url => requestPromise(url))).then(values => {
console.log("counter => ",values.length);
resultArray = resultArray.concat(values);
console.log("values=> ",values);
console.log("resultArray=> ",resultArray);
});
}
httpRequest does not return a promise so you have to make one yourself, also your resultArray is not necessary:
const makeRequest = url => new Promise((resolve, reject) => httpRequest(url, (error, res) => error ? reject(error) : resolve(res)));
Promise.all(urls.map(makeRequest))
.then(result => {
console.log(result.map(res => res.body.toString()));
console.log('called');
});

Categories