I am new to JavasSript's async, await and promise features.
What I am doing is,
async function sendTextMessage(text) {
console.log("----1----");
var messageData = {
message: {
text: text
}
};
await callSendAPI(messageData);
}
async function sendImageMessage(imageUrl) {
console.log("----2----");
var messageData = {
message: {
url: imageUrl
}
};
await callSendAPI(messageData);
}
async function sendQuickReply(replies) {
console.log("----3----");
var messageData = {
message: {
text: text,
quick_replies: replies
}
};
await callSendAPI(messageData);
}
async function callSendAPI(messageData) {
await request({
uri: 'https://graph.facebook.com/v2.6/me/messages',
qs: {
access_token: config.FB_PAGE_TOKEN
},
method: 'POST',
json: messageData
}, function(error, response, body) {
if (!error && response.statusCode == 200) {
var recipientId = body.recipient_id;
var messageId = body.message_id;
if (messageId) {
console.log("Successfully sent message with id %s to recipient %s",
messageId, recipientId);
} else {
console.log("Successfully called Send API for recipient %s",
recipientId);
}
} else {
console.error("Failed calling Send API", response.statusCode, response.statusMessage, body.error);
}
});
}
I am calling this functions in one switch case like,
async function send(){
await sendTextMessage(text);
await sendImageMessage(img);
await sendQuickReply(replies)
}
send()
But while response showssendImageMessage() last because this function may be not getting ready when I am sending imageUrl to generate a response.
Note this answer was written against the original question and not later edits made by the OP.
An async function is asynchronous. Other functions will not wait for it.
So you call sendTextMessage which calls callSendAPI and then the rest of the program carries on.
callSendAPI runs asynchronously. It calls request and waits for the promise returned by request to be resolved. When it has resolved, callSendAPI picks up the return value of request (well, it would if you captured the return value) and then continues with the next line (only there isn't a next line).
async / await do not make asynchronous code synchronous. They just make it look like it in inside the function declared as async which, itself, becomes asynchronous.
You could put your three function calls in an async function of their own, make sure each one returns a Promise, and then call each of those three with await.
See also How do I return the response from an asynchronous call?.
I hope that all you want todo is process some operations in strict order.
This is why async and await in ES6 exists.
So by example it may be like this do A then do B then do C or A > B > C
As you can see the last method do3 has only 500 ms delay but it is executed as last and not as first one.
Without async and await operators it would be executed as first function and this is default behavior of JavaScript execution. Broser will execute sync code as first and async after. But not with async and await anymore :)
You should also know that if you prefix string, number of function with await then the will be transformed into promise automatically.
The console will print :
'juraj1'
'juraj2'
'juraj3'
here is simple example :
function do1() {
return new Promise(resolve => {
return setTimeout(() => resolve("juraj1"), 3000);
});
}
function do2() {
return new Promise(resolve => {
return setTimeout(() => resolve("juraj2"), 2000);
});
}
function do3() {
return new Promise(resolve => {
return setTimeout(() => resolve("juraj3"), 500);
});
}
async function ForceAsynTOBeSync() {
const a = await do1();
console.log(a);
const b = await do2();
console.log(b);
const c = await do3();
console.log(c);
}
ForceAsynTOBeSync();
You have to await the API calls:
await callSendAPI(messageData);
And therefore the functions these calls are in need to be async too and awaited when called and so on.
Related
I want to wait on the HTTP POST request to complete and then return response to the caller function. I am getting Undefined when I print the received results.
I have defined post method as below:
// httpFile.js
const axios = require('axios');
module.exports = {
getPostResult: async function(params) {
console.log("getPostResult async called...");
var result = await axios.post("https://post.some.url", params)
.then ((response) => {
console.log("getPostResult async success");
return {response.data.param};
})
.catch ((error) => {
console.log("getPostResult async failed");
return {error.response.data.param};
});
}
}
And I am calling it in this way:
// someFile.js
const httpFile = require('./httpFile');
// Called on some ext. event
async function getPostResult() {
var params = {var: 1};
var result = await httpFile.getPostResult(params);
// Getting Undefined
console.log("Done Result: " + JSON.stringify(result));
}
I don't want to handle the .then and .catch in the calling function as I want to return the different values based on the POST result.
How should I wait for the response and get the return results.
In the above code I am getting the log statements as expected and "Done Result" get printed at the very end after the 'getPostResult' returns.
you are using both await & .then thats why it returns undefined.
this is how it should look
// httpFile.js
const axios = require('axios')
module.exports = {
getPostResult: async function (params) {
try {
const res = await axios.post('https://post.some.url', params)
return res.data
} catch (error) {
// I wouldn't recommend catching error,
// since there is no way to distinguish between response & error
return error.response.data
}
},
}
if you want to catch error outside of this function then this is way to go.
getPostResult: async function (params) {
const res = await axios.post('https://post.some.url', params)
return res.data
},
I'm using a combination of Jest and Supertest to test an API endpoint. I'm using the beforeAll() function of Jest to call the endpoint once before a collection of tests. Before I call the endpoint, I'm reading the request body from a file using fs.readFile.
Whatever I try, I cannot seem to await the result of my function that calls fs.readFile. My request is resulting in a 400 response every time, since the function readRequestBody is not being awaited. It seems that the program flow is continuing without awaiting the result and, therefore, sending an empty request body.
Code:
describe("Test POST call " + process.env.ENV, () => {
const url = config.apiURL;
let responseData: request.Response;
beforeAll(async (done) => {
const requestBody = await readRequestBody();
responseData = await request(config.apiURL)
.post("/v1.0/subjects/courses")
.send(requestBody)
.accept("application/vnd.api+json")
.set("content-type", "application/vnd.api+json");
done();
});
test("authorized should return 201 status code", () => {
expect(responseData.status).toBe(201);
});
});
async function readRequestBody() : Promise<string> {
let requestBody: string = "";
fs.readFile("./request.json", "utf8", (err, req) => {
if (err) {
console.log("Error loading request: " + err.message)
}
requestBody = req.replace("{{newCourseUuid}}", uuid.v4());
});
return requestBody;
}
I understand that fs.readFile reads the contents of a file asynchronously, but it looks like I'm not awaiting the results correctly. What am I missing? Is this something related to the fact that beforeAll is, itself, an asynchronous function?
try await fs.promises.readFile('file.txt') instead 👍
https://nodejs.org/api/fs.html#fs_fs_promises_api
async functions implicitly convert their return value to a promise. So your function signature async function readRequestBody() : Promise<string> means readRequestBody will return a Promise to create Promise to read the body. i.e. Promise<Promise<String>>. Instead you need to either remove the async keyword or the Promise from your return value.
Also your implementation of the function is incorrect as it will always return an empty string since fs.readFile is an asynchronous function.
Here is something that might fix both your issues:
function readRequestBody() : Promise<string> {
return new Promise((resolve, reject) => {
fs.readFile("./request.json", "utf8", (err, req) => {
if (err) {
console.log("Error loading request: " + err.message)
reject(err)
}
let requestBody: string = "";
requestBody = req.replace("{{newCourseUuid}}", uuid.v4());
resolve(requestBody)
});
});
}
This question already has answers here:
Waiting for more than one concurrent await operation
(4 answers)
Wait for all different promise to finish nodejs (async await)
(4 answers)
Closed 3 years ago.
I'm not sure what's the best way to handle async / await here, it doesn't seem like my application is waiting before it calls render so it's trying to load before the data is ready.
data.js:
this.asyncFunction = async (x) => { return await _makeRestcall(x) }
this.load = async function(id) {
this.data = {
params: await asyncFunction("restCall1").value.reduce( // transform data),
ideation: await asyncFunction("restCall2").value.reduce( // transform data),
roles: await asyncFunction("restCall3").value.reduce( // transform data),
serviceGroups: await asyncFunction("restCall4").value.reduce( // transform data),
allocationPercents: [],
maintenanceYears: [0, 3, 5]
};
return this.data;
};
async init() {
this.d = await this.load();
console.log("called data async");
}
app.js
import data from 'data'
await data.init();
render()
Ideally I'd like all the calls in data to run in parallel then return this.data when all the calls are done.
I ran a little test for you. I would recommend going with Promise.all, it can dispatch two async functions, but wait for them at the same place without blocking the function after each dispatch.
See the code below:
getFromAPIA();
getFromAPIB();
// A
async function getFromAPIA() {
console.log('starting A');
try {
const [a, b] = await Promise.all([asyncFunction(), asyncFunction()])
let data = {
a,
b,
}
console.log('A RES:', data);
return data;
} catch (err) {
return false; // Handle the error here in case the Promise returned a Reject
}
}
// B
async function getFromAPIB() {
console.log('starting B');
try {
let data = {
a: await asyncFunction(),
b: await asyncFunction(),
}
console.log('B RES:', data);
return data;
} catch (err) {
return false; // Handle the error here in case the Promise returned a Reject
}
}
// MIMIC ASYNC
function asyncFunction() {
return new Promise((res, rej) => {
setTimeout(() => {
res('Hello');
}, 10000)
})
};
Method B is basically what you did, which took double the time to get a response,
While Method A is with Promise.all. Now, promise all takes an array of promises as a parameter and also returns an array, so you will have to know the order of your dispatches in order to construct your object the way you want.
it doesn't seem like my application is waiting before it calls render so it's trying to load before the data is ready.
you're waiting for the wrong object, you're waiting for Promise.value.reduce
this.asyncFunction = async (x) => { return await _makeRestcall(x) }
this.load = async function(id) {
this.data = {
params: (await asyncFunction("restCall1")).value.reduce( // transform data),
ideation: (await asyncFunction("restCall2")).value.reduce( // transform data),
roles: (await asyncFunction("restCall3")).value.reduce( // transform data),
serviceGroups: (await asyncFunction("restCall4")).value.reduce( // transform data),
allocationPercents: [],
maintenanceYears: [0, 3, 5]
};
return this.data;
};
async init() {
this.d = await this.load();
console.log("called data async");
}
(Forgive me if the title is inaccurate to the problem this one is boggling the mind)
My application requires that I check a value from the request against the database. For this I created an asynchronous function to query the database:
async function checktoken(){
return prisma.$exists.invalidtoken({token: "testtoken"}).then(result => {
return(result)
})
}
I know that the database call on its own works:
prisma.$exists.invalidtoken({token: "testtoken"}).then(result => {
console.log(result) // returns true
})
In the function that fires at every request I try and call checktoken():
async function getUser(token){
var promise = await checktoken()
var result = promise
console.log(result) //undefined
};
Amending the function to include an explicit call to the database works but only when var promise = await checktoken() is defined before it:
async function getUser(token){
var promise = await checktoken() //When this is removed result1 is undefinded
await prisma.$exists.invalidtoken({token: "testtoken"}).then(result1 => {
console.log("inside db call: "+result1) // true
})
};
I think I have a fundamental misunderstanding of async/await but I am not sure exactly what I am missing.
EDIT:
I have updated my approach taking the advice I received and it still does not work. I am beginning to think my ORM is doing something weird:
async function test(token) {
const status = await prisma.$exists.invalidtoken({ token: token });
console.log(status);
return status;
}
test("123") //logs false (as it should)
async function getUser(token){
var status = await test(token) //logs undefined
console.log(status) //logs undefined
};
An async function requires an await. If you are using the promise.then() technique, then what you would want to do is return a new Promise(), and within the .then call back function resolve the promise
function checktoken() {
return new Promise((resolve,reject) => {
prisma.$exists.invalidtoken({token: "testtoken"}).then(result => {
doSomeOtherStuff();
resolve(result);
});
});
}
Is functionally the same as
async checktoken() {
await prisma.$exists.invalidtoken({token: "testtoken"});
}
I have a function called GetAllData() that calls GetPurchaseData, which recursively calls itself until it loads all the data. In
async function GetAllData(){
console.log("starting loading purchase data");
await GetPurchaseData();
console.log("purchase data loaded")
}
async function GetPurchaseData(){
return new Promise(async function (resolve,reject){
var Headers = {
....
}
await request({url: xxx, headers: Headers },async function(error, response, body) {
var tmp = JSON.parse(body)
_.forEach(tmp.Purchases, p => purchaseData.push(p));
if (response.headers.pagination){
return await GetPurchasePaginatedData()
}
else{
console.log("done loading....")
return resolve("done")
}
});
})
}
Node JS prints the following output:
starting loading purchase data
done loading....
but it never comes back up to GetAllData to print
purchase data loaded
it almost seems like its stuck in function, but my opinion is that somehow the line of "return resolve("done")" does not get back up to the initial call to actually mark the Promise as complete.
Avoid the async/await Promise constructor antipattern (see also here), and avoid passing an async function as a regular callback - you need to use the Promise constructor to promisify an existing callback API!
async function GetPurchaseData() {
var headers = {…};
var promise = new Promise((resolve,reject) => { // not async!
request({url: xxx, headers}, (error, response, body) => { // not async!
if (error) reject(error);
else resolve({response, body});
});
}); // that's it!
var {response, body} = await promise;
for (var p of JSON.parse(body).Purchases)
purchaseData.push(p));
if (response.headers.pagination) {
return GetPurchasePaginatedData()
} else {
console.log("done loading....")
return "done";
}
}
I don't have much experience with async/await, but from what I've read, shouldn't the code look something like this.
async function GetAllData(){
console.log("starting loading purchase data");
await GetPurchaseData();
console.log("purchase data loaded")
}
async function GetPurchaseData(){
let body = await request({url: xxx, headers: Headers })
var tmp = JSON.parse(body)
_.forEach(tmp.Purchases, p => purchaseData.push(p));
if (response.headers.pagination){
return await GetPurchasePaginatedData()
}
else{
console.log("done loading....")
return "done"
}
}