How to use Async and await inside of a loop - node.js

I am using Firebase and Nodejs in order to calculate some values. There are 40+ JSON objects will be there in the variable 'data'. 'getOHLCofStock' function expects a value and it will return a promise. But what happens here is, that the console.log function gets executed without waiting for the promise to resolve. Therefore, I am getting 'undefined' in the console.
Please help me to make this function wait and console the real value.
const path = db.ref(now);
path.on('value', async(snapshot) => {
for (const property in data) {
await zerodha.getOHLCofStock(property).then(async ohlc => {
console.log(ohlc);
//ohlc is 'undefined', but in fact, its not undefined. The 'await'
is not working here.
I want 'getOHLCofStock' should wait until the value gets resolved and
then only the console.log should execute.
})
}
})

Related

Node.js .map function causing express server to return the response object early

My goal is to create an abstracted POST function for an express running on Node similar to Django's inbuilt REST methods. As a part of my abstracted POST function I'm checking the database (mongo) for valid foreign keys, duplicates, etc..., which is where we are in the process here. The function below is the one that actually makes the first calls to mongo to check that the incoming foreign keys actually exist in the tables/collections that they should exist in.
In short, the inbuilt response functionality inside the native .map function seems to be causing an early return from the called/subsidiary functions, and yet still continuing on inside the called/subsidiary functions after the early return happens.
Here is the code:
const db = require(`../../models`)
const findInDB_by_id = async params => {
console.log(`querying db`)
const records = await Promise.all(Object.keys(params).map(function(table){
return db[table].find({
_id: {
$in: params[table]._ids
}
})
}))
console.log('done querying the db')
// do stuff with records
return records
}
// call await findIndDB_by_id and do other stuff
// eventually return the response object
And here are the server logs
querying db
POST <route> <status code> 49.810 ms - 9 //<- and this is the appropriate response
done querying the db
... other stuff
When I the function is modified so that the map function doesn't return anything, (a) it doesn't query the database, and (b) doesn't return the express response object early. So by modifying this:
const records = await Promise.all(Object.keys(params).map(function(table){
return db[table].find({ // going to delete the `return` command here
_id: {
$in: params[table]._ids
}
})
}))
to this
const records = await Promise.all(Object.keys(params).map(function(table){
db[table].find({ // not returning this out of map
_id: {
$in: params[table]._ids
}
})
}))
the server logs change to:
querying db
done querying the db
... other stuff
POST <route> <status code> 49.810 ms - 9 // <-appropriate reponse
But then I'm not actually building my query, so the query response is empty. I'm experiencing this behavior with the anonymous map function being an arrow function of this format .map(table => (...)) as well.
Any ideas what's going on, why, or suggestions?
Your first version of your code is working as expected and the logs are as expected.
All async function return a promise. So findInDB_by_id() will always return a promise. In fact, they return a promise as soon as the first await inside the function is encountered as the calling code continues to run. The calling code itself needs to use await or .then() to get the resolved value from that promise.
Then, some time later, when you do a return someValue in that function, then someValue becomes the resolved value of that promise and that promise will resolve, allowing the calling code to be notified via await or .then() that the final result is now ready.
await only suspends execution of the async function that it is in. It does not cause the caller to be blocked at all. The caller still has to to deal with a promise returned from the async function.
The second version of your code just runs the db queries open loop with no control and no ability to collect results or process errors. This is often referred to as "fire and forget". The rest of your code continues to execute without regard for anything happening in those db queries. It's highly unlikely that the second version of code is correct. While there are a very few situations where "fire and forget" is appropriate, I will always want to at least log errors in such a situation, but since it appears you want results, this cannot be correct
You should try something like as when it encounter first async function it start executing rest of the code
let promiseArr = []
Object.keys(params).map(function(table){
promiseArr.push(db[table].find({
_id: {
$in: params[table]._ids
}
}))
})
let [records] = await Promise.all(promiseArr)
and if you still want to use map approach
await Promise.all(Object.keys(params).map(async function(table){
return await db[table].find({
_id: {
$in: params[table]._ids
}
})
})
)

Cannot bring data from Firestore in Dialogflow Fulfillment (In-line Editor)

I am using Dialogflow to build an Action for Google Assistant. Everything works, except the Fulfillment of my intent.
I am using the Inline Editor (Powered by Cloud Functions for Firebase) to fulfill the intent. My function in itself runs - since I can send text to the assistant from the function.
But for some reason, code execution never enters the the function that fetches data from my Collection on Firebase Firestore - although it does execute commands before and after.
Here is the code in my index.js.
'use strict';
const admin = require('firebase-admin');
const functions = require('firebase-functions');
admin.initializeApp(functions.config().firebase);
let db = admin.firestore();
const {dialogflow} = require('actions-on-google');
const app = dialogflow({debug: true});
app.intent('INTENT', (conv, {ENTITY}) => {
conv.add("Hello."); //THIS IS DISPLAYED
db.collection("COLLECTION").orderBy("FIELD", "desc").get().then(snapshot => {
conv.add("Hey!"); //THIS IS NOT DISPLAYED
snapshot.forEach(doc => {
conv.add("Hi?"); //NOR IS THIS
});
conv.add("Hmm..."); //NEITHER THIS
}).catch(error => {
conv.add('Error!'); //NOT EVEN THIS
});
conv.add("Bye."); //THIS IS DISPLAYED TOO
});
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
Clearly, this shows that execution never really entered the db... block, and hence the function didn't even throw any error.
Here are the logs from Firebase.
Function execution started
Billing account not configured...
Warning, estimating Firebase Config based on GCLOUD_PROJECT. Initializing firebase-admin may fail
Request {...}
Headers {...}
Conversation {...}
Response {...}
Function execution took 1681 ms, finished with status code: 200
I know that the firestore function fetches data asynchronously, but there
seems no way I could execute anything even inside its .then(...) block.
I also tried returning a Promise from the .then(...) block, and using a second .then(...) with it - which again didn't work.
var fetch = db.collection("COLLECTION").orderBy("FIELD", "desc").get().then(snapshot => {
conv.add("Hey!"); //NOT DISPLAYED
var responseArr = [];
snapshot.forEach(doc => {
conv.add("Hi?"); //NOT DISPLAYED
responseArr.push(doc);
});
conv.add("Hmm..."); //NOT DISPLAYED
return Promise.resolve(responseArr);
}).then(fetch => {
conv.add("Here?"); //NOT DISPLAYED
}).catch(error => {
conv.add('Error!'); //NOT DISPLAYED
});
Finally, I also tried putting the firestore function in a separate function, like this.
function getData(){
return db.collection("COLLECTION").orderBy("FIELD", "desc").get().then(snapshot => {
snapshot.forEach(doc => {
...
});
return data; //Manipulated from above. 'data' can be a string.
}).catch(error => {
return error;
});
}
app.intent('INTENT', (conv, {ENTITY}) => {
conv.add("Hello."); //THIS IS DISPLAYED
conv.add(getData()); //THIS IS NOT DISPLAYED
conv.add("Bye."); //THIS IS DISPLAYED
});
The problem is that you're doing an asynchronous operation (the call to get()), but you're not returning a Promise from the Intent Handler itself. The library requires you to return a Promise so it knows that there is an async operation taking place.
Returning a Promise from inside the then() portion isn't enough - that doesn't return a value from the handler, it just returns a value that is passed to the next then() function or (if it was the last one) as the return value of the entire Promise chain.
In your original code, this can be done just by returning the get().then().catch() chain. Something like this as your first line:
return db.collection("COLLECTION").orderBy("FIELD", "desc").get() // etc etc
In your second example, the fetch in your then() block is not the fetch that you think it is, and only confuses matters. Structured that way, you need to return the fetch from the let assignment.
Your third example is more complicated. The line
conv.add(getData());
doesn't even seem like it would work, on the surface, because it is returning a Promise, but you can't add a promise to the conv object. You would need to rewrite that part as
return getData()
.then( data => conv.add( data ) );
But that doesn't address how the "Bye" line would work. If you actually wanted "Bye" after the data, you would have to include it as part of the then() block.
In short, when dealing with async data, you need to
Make sure you understand how Promises work and make sure all async work is done using Promises.
Add all your data inside the then() portion of a Promise
Return a Promise correctly

How to fix - Promise { <pending> } in Node.js and use it as variable anywhere

I'm accessing google API for converting coordinates into detailed objects using node-geocoder library from npmjs. Everything went well and I'm getting the expected object from geocoder API. The problem started the moment when I thought of using the data outside the promise function. I want to use the values outside the promise/async-await function.
Below is the code I've tried, Pls take a look and help me. TIA...
function goecoderPromiseFunction() {
return new Promise(function (resolve, reject) {
geocoder.reverse({ lat: 45.767, lon: 4.833 })
.then(data => {
cityName = data[0].city;
resolve(cityName);
})
.catch(err => {
console.log(err);
});
});
}
async function app() {
var a = await goecoderPromiseFunction();
return a;
}
var a = app();
console.log("a->", a);
I expect the variable "a" should print the city name "Lyon", but it prints
a-> Promise { < pending > }
The promise returned by the app function is never consumed, that is why it remains in a pending state.
Call then on the app function to get the result :
app().then(a => console.log("a->", a));
You can also use async/await :
(async function() {
var a = await app();
console.log("a->", a);
})();
An asynchronous function actually returns a promise that 'resolves' to the function's return value. You are therefore assigning a promise to the value of a. If you are in the global scope, you obviously cannot use async/await so you need to use either a self-executing async function or you need to run
a.then(data => console.log('a->', data));
to get what you are looking for.
Find out more about async functions here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
It prints because console.log("a->", a); runs while promise haven't returned answer for a variable "a"
Note: you haven't used reject function, if there is an error you wont notice and may be that error was the required answer to be carried by variable "a" that's why it still pending i.e still waiting.
For more idea try to use reject function inside the catch block example reject(err)
instead of console it out as you've done

Accessing restAPI response outside function in nodejs

I want to be access array outside the function or outside the loop in nodejs. I written following code.
var result = [];
function setid (swfid){
crud.getswift(swfid).then(function (response) {
console.log("response",response);
result = response;
// res.send(response);
}).catch(function (err) {
return ("error:" + err);
});
console.log("result",result);
}
console.log("result",result);
But its returning null. your suggestions please
You wrote a new statement in the function call and therefore you scoped it. This is one of the things wrong there. Apart from that, as the first person commenting to this answer mentioned, you have an async call here. Therefore, you need to return a promise from setid and wait for the response to get the result.
You're mixing your Aysnc logic with Sync. You won't get the response outside the .then function scope because there's no response available at the time you're trying to get the results.
Try using a callback in the promise - You'd need to invoke the function in the promise callback and send the response as function param, then play with the data.
> Promise / API call etc
.then(() => gotDataCallBack(data));
gotDataCallBack(data){
// handle your data and logic here.
// this will make sure you have the data available before you move ahead with
your application/manipulation logic.
}

Response time difference with the usage of util.promisify

I was working on a project and I decided to convert everything to async/await. After learning how it works, I noticed that I could use the following without util.promisify().
await transporter.sendMail(message);
That happens because if we don't set the callback argument, the method returns a Promise. https://nodemailer.com/usage/
When I run it, it takes ~2000ms for the API to respond according to Postman but when I turn it into a Promise (with the help of util.promisify), it takes ~200ms to get a response, why is that?
await util.promisify(cb => transporter.sendMail(message, cb));
Am I doing something wrong or it's just promisify more optimized than the promise return?
That's because it's lying to you. You'll notice that your email isn't getting sent (so no waiting for emails which means less time).
util.promisify() does not return a Promise. It returns a function that returns a Promise:
const resultOfSendMail = transporter.sendMail(message); // notice, no await
console.log(resultOfSendMail instanceof Promise); // true
// however
const resultOfPromisify = util.promisify(cb => transporter.sendMail(message, cb));
// still no await
console.log(resultOfPromisify instanceof Promise); // false
console.log(resultOfPromisify instanceof Function); // true
The way you use Promisify is that you pass a function in, and you get a function out. Like so:
const sendMailAsync = util.promisify((msg, cb) => transporter.sendMail(msg, cb));
// sendMailAsync is a *function* that takes only message, and returns a Promise!
await sendMailAsync(message); // This returns a Promise! We can use await
You'll find that now, it takes approximately the same time to respond in both. And in both times, the email will actually be sent.

Resources