Excerpt of a lambda function using nodejs 12.
There is a promise (aws dynamo db document client query) nested inside a promise (node-fetch API request).
The first promise works fine. It executes the request, waits for the promise to resolve, and then enters the .then(...) to execute some code. Next, it executes the database query, but then it exits the whole function without waiting for the promise to resolve, even though there are still callbacks. It doesn't throw errors either. (I know the database query is being executed , as extra logging showed it's status to be <pending>)
When I try var dbscan = await db.scan(...) using await, I get the error await is only valid in async function. But the handler function is already async, and await is working for the fetch promise.
Q: how can I use await inside the .then(...) of the first promise?
Q: why is the process exiting without waiting for the promise to resolve?
exports.myhandler = async (event, context, callback) => {
var response = await fetch(url, {... my options ...})
.then(res => res.text())
.then(data => {
// do some stuff with the data ...
var dbscan = db.scan({...my params ...}).promise()
.then(res => {
// do some stuff with result ...
callback(null, <some message>);
}).catch(err => {
console.log(err);
callback(null, <some message>);
});
})
.catch(error => {
console.log('error', error);
callback(null, <some message>);
});
}
You have to add async to the beginning of the function in which you want to use await.
.then(async(data) => {
var dbscan = await ...
})
Since you're already using the async/await-syntax, you shouldn't have to resolve to using .then(..) chains.
You could flatten most of your code like this:
const response = await fetch(url, {... my options ...});
const data = await response.text();
// Do some stuff with the data...
const result = await db.scan({...my params ...});
// Do some stuff with the result...
Related
I'm doing some functional testing with mocha. I stored my functions name, parameters and sucess message in local JSON file. I am checking my database response with local JSON response.
I'm using .then and .catch everywhere. I am looking to clean up a code with async await. How I can use async await here?
it('Check Authentication', (done) => {
readFileData('checkAuth').then(({ params, message}) => {
login.checkAuth({ ...params })
.then((result) => {
assert(result.message === message);
done();
})
.catch((err) => done(err));
});
});
Something like this. Haven't tested it tho. Basically instead of .then() you just await the call. Note that there is async before (done) callback. .catch() can be caught with try/catch block.
it('Check Authentication', async (done) => {
let response = await readFileData('checkAuth');
try {
let message = await login.checkAuth({ ...response.params }); // or w/e the response is
// assert the message
} catch (e) {
// do something with the error
}
});
Changed callback function to async to use `await
Wrapped all await calls in try-catch block to handle errors
Used const for params, message and result variables but if you are going to reassign values later in the code you can use let instead.
done() will be async call. Add await in front of that if you need that too to be sync call
it('Check Authentication', async (done) => {
try {
const { params, message } = await readFileData('checkAuth');
const result = await login.checkAuth({ ...params });
assert(result.message === message);
done();
} catch (err) {
done(err);
}
});
On the way of learning the concepts of asynchronous JavaScript, I got struggled with the idea behind the situation when they can be chained. As an example consider the following situation: a webhook calls a cloud function and as a requirement there is set a time interval by which the cloud function should response to the webhook. In the cloud function is called an operation for fetching some data from a database, which can be short- or long-running task. For this reason we may want to return a promise to the webhook just to "register" a future activity and later provide results from it e.g.:
async function main () {
return new Promise ((resolve, reject) => {
try {
db.getSomeData().then(data=> {
resolve({ result: data});
});
} catch (err) {
reject({ error: err.message });
}
});
}
Another way is to use async/await instead of a promise for fetching the data, e.g.:
async function main () {
return new Promise (async (resolve, reject) => {
try {
const data = await db.getSomeData();
resolve({ result: data });
} catch (err) {
reject({ error: err.message });
}
});
}
(The code is a pseudo-code and therefore all checks on the returned types are not considered as important.)
Does the await block the code in the second example and prevent returning of the promise?
async functions always return a Promise. It is up to the function caller to determine how to handle it: either by chaining with then/catch or using await. In your example, there is no need to create and return a new Promise.
You could do something like this for example:
async function main () {
try {
const data = await db.getSomeData()
return {result: data}
} catch (err) {
return {error: err.message}
}
}
// On some other part of the code, you could handle this function
// in one of the following ways:
//
// 1.
// Chaining the Promise with `then/catch`.
main()
.then((result) => console.log(result)) // {result: data}
.catch((err) => console.log(err)) // {error: err.message}
// 2.
// Using await (provided we are working inside an asyn function)
try {
const result = await main()
console.log(result) // {result: data}
} catch (err) {
console.log(err) // {error: err.message}
}
Try to avoid combining both methods of handling asynchronous operations as a best practice.
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)
});
});
}
Imagine the following hypothetical, minimal implementation of a function that does a simple HTTP GET request using axios. This uses await/async as depicted in the post.
const axios = require('axios')
exports.getStatus = async id => {
const resp = await axios.get(
`https://api.example.com/status/${id}`
)
return resp
}
Is the promise not resolved using await? Is it required that the client uses await as depicted below? Is it safe to assume that anytime a client consumes an async function, that it also needs to use await when calling the function?
// client
const { getStatus } = require('./status')
const response = await getStatus(4)
Short answer, no.
Labelling a function async means 2 things:
1) you're allowed to use await inside the function.
2) The function returns a promise; i.e. you can use await on its return value, or you can use .then, or Promise.all, or even ignore the return value, or anything else you can do with promises.
E.g. you could do the following which (depending on your use case) could be more performant because it needs not wait for the response to continue.
// client
const { getStatus } = require('./status')
const response4 = getStatus(4);
const response5 = getStatus(5);
// do stuff that don't rely on the responses.
response4.then(response => myOutput.setFirstOutput(response));
response5.then(response => myOutput.setSecondOutput(response));
Btw, your first snippet is redundant with its usage of await; it's equivalent to
const axios = require('axios')
exports.getStatus = id =>
axios.get(`https://api.example.com/status/${id}`);
This is because return await promise is equivalent to return promise, both return the same promise. There is one exception regarding rejected promises. Awaiting a rejected promise will throw an exception (which you can catch with a try/catch block), whereas directly returning a rejected promise will not throw an exception but you should still handle the rejection (with a .catch clause). To illustrate:
let promise = new Promise(resolve => setTimeout(() => resolve('resolved after 2 seconds'), 2000));
let returnPromise = () => promise;
let returnAwaitPromise = async () => await promise;
returnPromise().then(value => console.log('returnPromise,', value));
returnAwaitPromise().then(value => console.log('returnAwaitPromise,', value));
No, you don't need to use await. An async function returns a Promise that should be resolved. Instead of using await, you can simply call .then() like so:
// client
const { getStatus } = require('./status');
getStatus(4).then((response) => {
//Do something with the response
}).catch((error) => {
//Handle possible exceptions
});
Below I'll address your question from the comments. The getStatus function could be rewritten like this:
exports.getStatus = (id) => {
return new Promise((resolve, reject) => {
axios.get(`https://api.example.com/status/${id}`).then((resp) => {
resolve(resp);
}).catch((error) => {
reject(error);
})
});
}
It would act exactly the same as in your version. When you call asynchronous functions in series, you have to resolve each of the returned Promises. In your case, there are two of them - the one returned by axios.get and the one returned by getStatus.
No you don’t have to. The async function executes and waits until it returns error or completes execution and return a response!
I am making a fetch API call from my js file ,I have a doubt that when i am using Async/await still the code is executing in asynchronous manner.
I have also tried await at differnt place but its doesn't work.
let info
async function weather(){
// API call
let data=await fetch('http://api.openweathermap.org/data/2.5/weather?'+'&lat=20&lon=44'+'&units=metric'+'&APPID='+WEATHER_KEY)
console.log("inside API")
const res= await data.json();
console.log(res)
}
weather()
console.log("last part")
OUTPUT:
last part
inside API
"VALUE OF RES"
WHAT I EXPECT:
inside API
"VALUE OF RES"
last part
Any help will be highly appreciated..
The easiest way is to wrap it all in another async function so that you can await weather().
// this function fetches the weather then logs the result
async function weather() {
// API call
let data = await fetch('http://api.openweathermap.org/data/2.5/weather?'+'&lat=20&lon=44'+'&units=metric'+'&APPID='+WEATHER_KEY);
console.log('inside API');
const res = await data.json();
console.log(res);
}
// this async function awaits for weather() to return
async function run() {
await weather();
console.log('last part');
}
// this runs first
run();
// async function
async function fetchAsync () {
// await response of fetch call
let response = await fetch('http://api.openweathermap.org/data/2.5/weather?'+'&lat=20&lon=44'+'&units=metric'+'&APPID='+WEATHER_KEY);
// only proceed once promise is resolved
let data = await response.json();
// only proceed once second promise is resolved
return data;
}
// trigger async function
// log response or catch error of fetch promise
fetchAsync()
.then(data => console.log(data))
.catch(err => console.log(err))
A solution in a script that run in node environment:
(async function () {
// API call
const data = await fetch('http://api.openweathermap.org/data/2.5/weather?'+'&lat=20&lon=44'+'&units=metric'+'&APPID='+WEATHER_KEY)
console.log("inside API")
const res = await data.json();
console.log(res)
console.log("last part")
})()
The messages will are in expected order.