I have a callback, where I need to get the array out of my callback.
I am trying to return the awaited array into the predefined one.
let documents = [];
socket.on('echo', async function(documents, data){
documents = await data;
console.log(documents); // shows me, what I want to see
});
console.log(documents); // empty Array
I need the result in my predefined Array documents
I have read several Tuts, but I dont get it. I know on stackoverflow it is sked several times. But all threads seem to be more complex then my situation. So I hope to get it cleared out with an more incomplex one.
You need to understand something first. When this runs what is inside the callback doesn't run unit the server will emit that event, in your case 'echo'.
What I think you want to do is use documents outside the callback. You can create a function and call it when the event is emitted.
Something like this:
const manageDocuments = (documents) => {
// do what you want with documents array
console.log(documents);
}
socket.on('echo', async function(documents, data){
documents = await data;
manageDocuments(documents);
});
Of course you can also get rid of the async/await
let documents = await socket.on('echo', async function(documents, data){
console.log(documents);
return data;
});
console.log(documents);
The problem is that the code outside the socket function executes with the empty array because is only executed once at runtime.
If you want to have access to the documents inside the socket function you have to make then persist, or use the socket.on inside of another loop.
Related
allDoc.map(function(stage){
for(let key of stage.layerItem){
//key.toImage is a library, so it only supports callback.
key.toImage({
callback(img) {
addPhoto(key.attrs)
}
});
}
})
// Run when addPhoto is done
console.log("done")
async function addPhoto(params){
const stored = await s3.upload(params).promise()
return stored.Location
}
When the addPhoto function is completed, how do I proceed to the next code?
First wrap the toImage function in one that returns a promise then use Promise.all to wait for them all to settle.
(Community Wiki because this is close to being a duplicate question but has two distinct steps each of which has their own duplicate).
One of the solutions I found for waiting .map() to be complete, is putting a Promise.all().
Your code should look like this:
const promisse = allDoc.map(function(stage, index){
// Your loop
})
await Promise.all(promisse);
console.log('done');
Keep in mind that if your allDoc.map is inside another function, the upper function must be async.
I am working on a database migration. This requires querying one DB, getting an array of records and perform a set of async operations to insert the data in the new DB. In order to keep data consistency, I want to insert the records one at the time so I want each operation to run sequentially. The only way I have found to do these is using recursion.
Is there a cleaner way of doing this same thing? I know there is a library called async https://caolan.github.io/async/v3/ which I never tried before.
The recursive method I have written looks like this:
const insertItem = async (data) => {
let item = data[0];
if (!item) {
//I am done return
return;
}
try {
//Do multiple await calls to insert record into new database
} catch (e) {
//Recover from error (DB rollbacks, etc)
} finally {
//Remove inserted or failed item from collection.
data.shift();
await insertItem(data);
}
};
//Query original database
getInfo().then((data) => insertItem(data));
You can use sync loop for...of it will wait for HTTP response.
const dataArr = ['data1', 'data2', 'data3'];
async function processItems(arr){
for(const el of arr) {
const response = await insertData(el);
// add some code here to process the response.
}
};
processItems(dataArr);
The posted code achieves iterating through the data collection (an array) retrieved from the first data base by using data.shift to mutate the argument array before calling the single function handling everything recursively.
To clean this up, remove the shift and recursive calls by splitting the data processing function into two:
one function to step through data retrieved from the first data base,
a second function to insert records in the second data base as required.
This removes the need for the .finally clause and leaves the structure of the code looking more like
async function insertData(data) {
for( let index = 0 ; index < data.length; ++index) {
await insertItem( data[index]);
}
}
async function insertItem( data) {
try {
//Do multiple await calls to insert record into new database
} catch (e) {
//Recover from error (DB rollbacks, etc)
// throwing an error here aborts the caller, insertData()
}
}
getInfo().then( insertData).catch( /*... handle fatal error ...*/);
Depending on preferred style, insertItem could be declared as nested function within insertData to keep it looking neat, and insertData could be written as an anonymous function argument of the then call after getInfo().
It is possible, of course, to perform asynchronous operations sequentially be other means, but using await inside an async function is perhaps the simplest coding method.
I hope this is not a silly question. I have a homepage route that loads up a lot of mongo databases, and I originally had it loop through the mongo databases and add them to an array that was rendered to a webpage. However, the databases have become more complicated and they need to be populated, so I can no longer use a loop to accomplish this, and needed to refactor to getting the databases individually. However, I seem to be having problems with the variable's scopes, as they are always returned as empty outside of the .find function.
My original code was this:
const collections = [User, Ticket, Client, Job, Transaction];
let endCollections = [];
for (let i = 0; i < collections.length; i++){
await collections[i].find({}, function(err, foundCollection){
if (err) {
console.log(err);
} else {
endCollections[i] = foundCollection;
}
});
}
res.render("dashboard", {transactions: endCollections[4], clients: endCollections[2], tickets: endCollections[1], jobs: endCollections[3]});
And this worked fine. But I need to populate the individual databases, so this was no longer useful. I rewrote it out to populate, but I am having problems changing the global variables inside of the functions. Here is the new code I am trying:
let transactions = [],
clients = [],
jobs = [],
tickets = [];
await Transaction.find({}).populate("job").populate("client").populate("deposited_by_user").exec(function(err, foundTransactions){
if(err){
console.log(err)
} else {
for (let i = 0; i < foundTransactions.length; i++){
foundTransactions[i]["transaction_info"]["new_amount"] = numberWithCommas(foundTransactions[i]["transaction_info"]["amount"]);
}
}
transactions = foundTransactions;
});
await Client.find({}).populate("transactions").populate("jobs").exec(function(err, foundClients){
if(err){
console.log(err)
}
clients = foundClients;
});
await Ticket.find({}).populate("created_by").populate("assigned_user").populate("completed_by_user").exec(function(err, foundTickets){
if(err){
console.log(err)
}
tickets = foundTickets;
});
await Job.find({}).populate("created_by").populate("client").populate("transactions").exec(function(err, foundJobs){
if(err){
console.log(err)
}
jobs = foundJobs;
});
res.render("dashboard", {transactions: transactions, clients: clients, tickets: tickets, jobs: jobs});
For example, if I console.log "jobs" right after the line jobs = foundJobs;, it will show the jobs array being populated. However, if I console.log "jobs" right before the res.render, it shows it as empty. Considering the global variable endCollections in my original code seemed to be changed within the functions before, I am unsure why my new code does not do the same as everything is returned empty. I know that somehow the scope of the variable is what is wrong here, but I cannot see how. Is there something obvious I am missing? Thanks.
Here now the answer so it is not buried in the post's comments.
After reading the docs, I think you should either use await with an empty exec() or use exec(callback).
What happens when you use both is that exec(callback) sees u passed a callback, it asynchronously executes your query and adds the callback to the promise.then of the query promise to be called once the query promise is settled. Then it immediately returns but it does not return the query promise since you passed a callback. The await is simply awaiting the normal (probably void/undefined) return of the function which is why removing it does not change anything.
After awaiting the return of the function, res.render executes and some time after that, the promise that had been created in the exec(callback) call settles and the callback you passed is executed.
So what is the appropriate way of fixing this? I would encourage you to read deeper into async/awai, promises, and the docs I linked above and find it out yourself before you read on, but since the solution is quite simple I'll leave it here.
// your variable declarations
try {
const foundTransactions = await Transaction.find({}).populate("job").populate("client").populate("deposited_by_user").exec();
// your for loop
transactions = foundTransactions;
// same for the other calls
tickets: tickets, jobs: jobs});
catch (e) {console.log(e);}
res.render("dashboard", {transactions: transactions, clients: clients,...
I have to compare few rows derived from potgres with data derived from the dynamo DB. problem is in both cases i have to iterate over data & unless i can assign the value to a variable i will have to query the same data again from either one of two databases.
function postgressdata(sql){
return new Promise(function(resolve, reject) {
client_postgres.query(sql, (err, res) => {
resolve(res);
});
});
}
let get_creatives = postgressdata(sql);
get_creatives.then(function(result) {
console.log(result); // prints the result
let new_result = result;
}
console.log(new_result) // does not print result
How Can I assign the data to a variable here & use it anywhere?
I am a newbie to NODEJS so pardon me if I am asking silly question.
Nodejs is non-blocking.
so when this block of code runs
let get_creatives = postgressdata(sql);
get_creatives.then(function(result) {
console.log(result); // prints the result
let new_result = result;
}
console.log(new_result) // does not print result
what will happen is you will get nothing printed first because .then() will be called when the promise is resolved so this line
console.log(new_result) // does not print result
will be executed first which doesn't have the value yet obviously.
after that when the promise is resolved .then will be called and your value will be set.
now how to solve this?
I am assuming you're sending the value back to the callee so you can either use the async/await structure or you can use a callback, depending on how you need the value.
You can just keep a reference to the promise as your cache. A promise in JavaScript represents the result of an async operation, not the operation itself.
If you have something like:
async function runQuery() {
const result = await db.query(sql`SELECT * FROM creatives;`);
console.log('ran query');
return result;
}
const creativesPromise = runQuery();
async function useResult() {
console.log(await creativesPromise);
}
useResult();
useResult();
useResult();
even though we use the resulting list of creatives three times, "ran query" will only get logged once. Every use of the query results will need to be async, because you never know whether the query has finished running when you request the data, but the query will only be run once.
I'm working with Cloud Functions for Firebase, and I get a timeout with some of my functions. I'm pretty new with JavaScript. It looks like I need to put a for inside a promise, and I get some problems. The code actually get off from for too early, and I think he make this in a long time. Do you have some way to improve this code and make the code faster?
exports.firebaseFunctions = functions.database.ref("mess/{pushId}").onUpdate(event => {
//first i get event val and a object inside a firebase
const original = event.data.val();
const users = original.uids; // THIS ITS ALL USERS UIDS!!
// so fist i get all users uids and put inside a array
let usersUids = [];
for (let key in users) {
usersUids.push(users[key]);
}
// so now i gonna make a promise for use all this uids and get token's device
//and save them inside a another child in firebase!!
return new Promise((resolve) => {
let userTokens = [];
usersUids.forEach(element => {
admin.database().ref('users/' + element).child('token').once('value', snapShot => {
if (snapShot.val()) { // if token exist put him inside a array
userTokens.push(snapShot.val());
}
})
})
resolve({
userTokens
})
}) // now i make then here, from get userTokens and save in another child inside a firebase database
.then((res) => {
return admin.database().ref("USERS/TOKENS").push({
userTokens: res,
})
})
})
You are making network requests with firebase, so maybe that's why it's slow. You are making one request per user, so if you have 100 ids there, it might as well take a while.
But there's another problem that I notice, that is: you are just resolving to an empty list. To wait for several promises, create an array of promises, and then use Promise.all to create a promise that waits for all of them in parallel.
When you call resolve, you have already done the forEach, and you have started every promise, but they have not been added to the list yet. To make it better, chance it to a map and collect all the returned promises, and then return Promise.all.