Getting constant value from asynchronous function - javascript

Here is my code.
const fbLoginData= {}
function _responseInfoCallbackID (error, result) {
if (error) {
fbLoginData.error="Unable to fetch data.Try again later or use other methods to sign-in"
}
else {
fbLoginData.id = result.id
fbLoginData.email=result.email
fbLoginData.name=result.name
}
}
function _responseInfoCallbackPicture (error, result) {
if (error) {
fbLoginData.error="Unable to fetch data.Try again later or use other methods to sign-in"
}
else {
fbLoginData.profile= result.data.url
}
}
export async function logInFB () {
try{
const login = await LoginManager.logInWithPermissions(["public_profile","email"])
const value = await getValue(login)
console.warn(" THE VALUE I AM SENDING ",value)
return value
}
catch(error)
{
fbLoginData.error= "Unable to fetch Data"
}
}
const getValue = (result)=> {
if (!result.isCancelled) {
return AccessToken.getCurrentAccessToken().then(
(data) => {
fbLoginData.accessToken = data.accessToken
const infoRequest = new GraphRequest(
'/me',
{
accessToken,
parameters: {
fields: {
string: 'email,name'
}
}
},
_responseInfoCallbackID,
);
const pictureRequest = new GraphRequest(
`/me/${PICTURE_PARAM}`,
{
accessToken,
},
_responseInfoCallbackPicture,
);
// Start the graph request.
new GraphRequestManager().addRequest(infoRequest).start()
new GraphRequestManager().addRequest(pictureRequest).start()
return fbLoginData
}
)
}
}
I want to have fbLoginData object to be filled with the data after having the callback functions _responseInfoCallbackID and _responseInfoCallbackPicture called. But the async function loginFB value doesn't return any other data than access token, but I want id, email, name that gets returned. I know the problem is due to asynchronicity, how can I get the fbLoginData with all the data I need? I cannot figure out the way I can do that. Any help on how to get the loginFB return the fbLoginData value with id,email,name and image that I want ?

This problem can be solved by following below steps:
Wrap the graph request's
new GraphRequestManager().addRequest(callback).start() inside a promise block
Create a global object promiseContainerand add the resolve and reject method of the above created promises to it.
Pass the above promises to Promise.all.
Resolve the promise inside of the callback function by using promiseContainer.
The Promise.all() method returns a single Promise that resolves when all of the promises passed as an iterable have resolved or when the iterable contains no promises.
const fbLoginData = {};
const promiseContainer = {};
function _responseInfoCallbackID(error, result) {
// Earlier Code
promiseContainer.infoPromise.resolve(true);
}
function _responseInfoCallbackPicture(error, result) {
// Earlier Code
promiseContainer.picturePromise.resolve(true);
}
// Earlier Code
const getValue = result => {
if (!result.isCancelled) {
return AccessToken.getCurrentAccessToken().then(data => {
// Earlier code
const infoPromise = new Promise(function(resolve, reject) {
promiseContainer['infoPromise'] = { resolve, reject };
new GraphRequestManager().addRequest(infoRequest).start();
});
const picturePromise = new Promise(function(resolve, reject) {
promiseContainer['picturePromise'] = { resolve, reject };
new GraphRequestManager().addRequest(pictureRequest).start();
});
return Promise.all([infoPromise, picturePromise]).then(() => fbLoginData);
});
}
};

Related

Resolve is returned before for loop is completed inside a Promise

I am very confused to why the the return resolve({ status: true, data: newIDs }); is called before the for loop has finished.
Code
createPallet: (data) => {
return new Promise(async (resolve, reject) => {
const newIDs = [];
try {
for (const record of data) {
mysqlConnection.beginTransaction();
mysqlConnection.query(
"INSERT INTO ...",
[record.BatchId, record.PalletNumber, record.PrimaryWeightId],
async (error, results) => {
if (error) {
return reject(error);
}
// Create pallet sku record
await PalletSkusService.createPalletSku(
...
);
console.log(results.insertId);
newIDs.push(results.insertId);
}
);
}
return resolve({ status: true, data: newIDs });
} catch (error) {
mysqlConnection.rollback();
return reject(error);
}
});
},
Expectation
I expect the for loop to get all the new inserted id's and store them into newIDs array. Once that is done, I can return a resolve with the data.
However, this part I don't quite understand is - why does the resolve is ran before the for loop has finished?
What is the correct approach in this situation?
mysqlConnection.query is returning immediately and not waiting for the data to return. I don't know what library you're using for this but you want to find the promise based method to accomplish this. Then, modify the loop so it waits for an array of promises to complete before resolving.
async createPallet(data) {
try {
// modified loop
const newIDs = await Promise.all(data.map((record) => {
mysqlConnection.beginTransaction();
// this is a best guess for how the a promise-style query would look
const await result = await mysqlConnection.query(
"INSERT INTO ...",
[record.BatchId, record.PalletNumber, record.PrimaryWeightId]
);
// Create pallet sku record
await PalletSkusService.createPalletSku(
...
);
console.log(results.insertId);
return results.insertId;
}));
return { status: true, data: newIDs };
} catch (error) {
mysqlConnection.rollback();
throw error;
}
},

How can I update mysql2 query to not return undefined for return?

I am using nodejs and mysql2. I am storing all my queries inside a class. When I console.log the results I am able to view the data correctly, however when I use a return statment to return the data, I am getting undefined. I believe I need to use promises, but am uncertain how to do so correctly. Here is what I have currently which is returning undefined,
viewManagerChoices() {
const sql = `SELECT CONCAT(first_name, ' ', last_name) AS manager, id FROM employee WHERE manager_id IS NULL`;
db.query(sql, (err, rows) => {
if (err) throw err;
const managers = rows.map(manager => ({ name: manager.manager, value: manager.id }));
managers.push({ name: 'None', value: null });
return managers;
});
};
This is my attempt at using promises which is returning as Promise {<pending>},
viewManagers() {
return new Promise((resolve, reject) => {
const sql = `SELECT CONCAT(first_name, ' ', last_name) AS manager FROM employee WHERE manager_id IS NULL`;
db.query(sql,
(error, results) => {
if (error) {
console.log('error', error);
reject(error);
}
const managers = [];
for (let i = 0; i < results.length; i++) {
managers.push({ name: results[i].manager, value: i+1 });
}
managers.push({ name: "None", value: null });
resolve(managers);
}
)
})
}
My class is called Query and I am calling these methods by doing,
const query = new Query();
query.viewManagerChoices();
query.viewManagers();
Your implementation for viewManagers is correct however promises don't make calls synchronous.
Either you need to use then callback or await the result in async context.
const query = new Query();
query.viewManagers().then((managers) => {
// do stuff here
}).catch((error) => console.error(error.message));
or
async someFunc() {
const query = new Query();
try{
const managers = await query.viewManagers();
}catch(error){
console.error(error.message);
}
}
Once you use promise you cannot just get a returned value without async/await or then flag. Once it's a promise the flow continues as the original.
For example:
// This is promise too because it has async flag.
// You cannot make it sync if you use promise in it
async myFunc(){
const const query = new Query();
const managers = await query.viewManagers();
return managers;
}
// It actually returns Promise<...>
// Now if you want to use myFunc in another function
// You need to do it the same way again
async anotherFunc(){
const something = await myFunc();
return something; // Returns promise
}
You can read more about promises here

How to return a promise function using node js

How can i return this function a promise
i want to return this function a promise function but don't know how to return a promise
return new Promise((resolve, reject) => {});
async function readFile(filePath) {
const myInterface = readline.createInterface({
input: fs.createReadStream(filePath),
});
const arrayLink = [],
arrayName = [];
for await (const line of myInterface) {
let temp = line.split(",");
arrayLink.push(temp[0]);
arrayName.push(temp[1]);
}
return [arrayLink, arrayName];
}
You can return a promise from the function. But in that case, to make that function async does not make any sense though it will work fine.
When a function returns a promise you don't need to write then block. You can handle it with await keyword. Whatever you provided in resolve() function, you will get that value in variable - result
If you want to return a promise from your function following is the way.
function readFile() {
return new Promise((resolve, reject)=>{
// your logic
if(Success condition met) {
resolve(object you want to return);
}else{
reject(error);
// you can add error message in this error as well
}
});
}
async function fileOperation() {
let result = await readFile();
}

Properly chaining functions in Firebase function

I am building a function in Firebase Cloud Functions, which can utilize Node.js modules.
I am still new to the use of .then() and I'm struggling to figure out a way to chain my 3 functions webhookSend(), emailSendgrid(), and removeSubmissionProcessor() that happen right after the 'count' is incremented (the if statement that checks temp_shouldSendWebhook). The whole idea of returning promises still confuses me a little, especially when it it involves external libraries.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const request = require('request');
const firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG);
const SENDGRID_API_KEY = firebaseConfig.sendgrid.key;
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(SENDGRID_API_KEY);
exports.submissionProcess = functions.database.ref('/submissions/processor/{submissionId}').onWrite((change, context) => {
var temp_metaSubmissionCount = 0; // omitted part of function correctly sets the count
var temp_shouldSendWebhook = true; // omitted part of function correctly sets the boolean
return admin.database().ref('/submissions/saved/'+'testuser'+'/'+'meta').child('count')
.set(temp_metaSubmissionCount + 1)
.then(() => {
// here is where im stuck
if (temp_shouldSendWebhook) {
webhookSend();
emailSendgrid();
removeSubmissionProcessor();
} else {
emailSendgrid();
removeSubmissionProcessor();
}
})
.catch(() => {
console.error("Error updating count")
});
});
function emailSendgrid() {
const user = 'test#example.com'
const name = 'Test name'
const msg = {
to: user,
from: 'hello#angularfirebase.com',
subject: 'New Follower',
// text: `Hey ${toName}. You have a new follower!!! `,
// html: `<strong>Hey ${toName}. You have a new follower!!!</strong>`,
// custom templates
templateId: 'your-template-id-1234',
substitutionWrappers: ['{{', '}}'],
substitutions: {
name: name
// and other custom properties here
}
};
return sgMail.send(msg)
}
function webhookSend() {
request.post(
{
url: 'URLHERE',
form: {test: "value"}
},
function (err, httpResponse, body) {
console.log('REQUEST RESPONSE', err, body);
}
);
}
function removeSubmissionProcessor() {
admin.database().ref('/submissions/processor').child('submissionkey').remove();
}
I want to be able to construct the 3 functions to be called one after another such that they will all execute.
In order to chain these functions, they each need to return a promise. When they do, you can call them sequentially like this:
return webhookSend()
.then(() => {
return emailSendgrid();
})
.then(() => {
return removeSubmissionProcessor();
});
Or in parallel like this:
return Promise.all([webhookSend, emailSendgrid, removeSubmissionProcessor]);
Now, to make your functions return promises:
emailSendgrid: It looks like this returns a promise (assuming sgMail.send(msg) returns a promise), so you shouldn't need to change this.
removeSubmissionProcessor: This calls a function that returns a promise, but doesn't return that promise. In other words it fires off an async call (admin.database....remove()) but doesn't wait for the response. If you add return before that call, this should work.
webhookSend calls a function that takes a callback, so you'll either need to use fetch (which is promise-based) instead of request, or you'll need to convert it to return a promise in order to chain it:
function webhookSend() {
return new Promise((resolve, reject) => {
request.post(
{
url: 'URLHERE',
form: {test: "value"}
},
function (err, httpResponse, body) {
console.log('REQUEST RESPONSE', err, body);
if (err) {
reject(err);
} else {
resolve(body);
}
}
);
});
}
Use async functions and then you can use .then() or await before every function calls
for reference read this

Async / Await, how do I force my function to wait until promise is resolved before it continues?

I would like my function to execute until the nextPageToken is null. When I execute the function the first time, it waits for the promise to be resolved. However as soon there is a nextPageToken present on the response, the function does not wait for the response and a stack overflow occurs.
It seems that f() is not suspended on the when await p.then() is called.
Am I totally misunderstanding how async/await works?
Any help would be greatly appreciated...
public apiResult2(path: string, objectName: string, params: any = { }) {
let returnArray = [];
const p = new Promise<any> ((resolve, reject) => {
gapi.load('auth2:client', () => {
gapi.client.request({
path: path,
params: params
}).then(response => {
// resolve this promise with the first key in the response object.
resolve(response.result);
}, reason => {
console.log(reason);
reject(reason.result);
});
});
});
let f = async () => {
let nextPageToken = null;
do {
let r = await p.then(result => {
if (result.hasOwnProperty(objectName)) {
for (let obj of result[objectName]) {
returnArray.push(obj);
}
}
if (result.hasOwnProperty('nextPageToken')) {
params.nextPageToken = result.nextPageToken;
return result.nextPageToken;
// nextPageToken = result.nextPageToken;
} else {
params.nextPageToken = null;
return null;
// nextPageToken = null;
}
});
nextPageToken = r;
console.log(r);
} while (nextPageToken);
};
f();
return returnArray;
}
If your function needs to "await" some async call, then it must also be async. Your function apiResult2 is not going to wait for f to be finished, in order to return returnArray.
EDIT:
The main issue here is that you are trying to reuse the promise p to make different requests, but this is not possible. The promise p will be initialized with the parameters for the first request, and all the calls to p.then will be fulfilled with the same result: the response for the first page request.
I made some small changes to your code, and got it working with a mocked interface:
const apiResult2 = async (path: string, objectName: string, params: any = { }) => {
const requestPage = async () => new Promise<any> ((resolve, reject) => {
gapi.load('auth2:client', () => {
gapi.client.request({
path: path,
params: params
}).then(response => {
// resolve this promise with the first key in the response object.
resolve(response.result);
}, reason => {
console.log(reason);
reject(reason.result);
});
});
});
let returnArray: string[] = [];
do {
const page = await requestPage();
if (page.hasOwnProperty(objectName)) {
for (let obj of page[objectName]) {
returnArray.push(obj);
}
}
if (page.hasOwnProperty('nextPageToken')) {
params.nextPageToken = page.nextPageToken;
} else {
params.nextPageToken = null;
}
} while (params.nextPageToken);
return returnArray;
}
Usage example:
apiResult2(path, objectName, params).then(
result => console.log(result),
err => console.log('error', err)
);

Categories