JavaScript: Using Async/Await in a Promise - javascript

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.

Related

Nodejs: process exits while promise is pending

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...

How to stream data using promises?

How can I stream data using promises.
I have two different functions in two different files. In one calls an API service and that returns a promise.
async myPromise(){
return new Promise((resolve, reject) => {
callToAnAPI().then(()=>{
resolve ("pending");
}).then(()=>{
resolve(callToAnotherAPI());
})
.catch(err=>{
// error handling
});
});
}
In another file I have a function like so:
async myPromise2(){
functionFromOtherFile().then((data)=>{
// how can I get 'pending' here?
}).then(data =>{
// how can I get data fromncallToAnotherAPI() here?
})
}
I want to know that the api has been called and that it is in 'pending' state. How can I achieve this?
Stream is just an async iterator... So we could just use callback, much like node.js
function myPromise(cl) {
cl(null, "pending")
setTimeout(() => {
cl(null, "data")
}, 2000);
}
function myPromise2() {
myPromise((err, data) => {
console.log(data)
})
}
myPromise2()
The most elegant way in my opinion is to return two promises and process them separately.
function myPromise(){
const api1Status = callToAnAPI().then(()=>{
resolve ("pending");
});
return [
api1status,
api1status.then(()=>{
resolve(callToAnotherAPI());
})
.catch(err=>{
// error handling
})
];
}
Then the second file would use it like this:
async myPromise2(){
const [api1, api2] = functionFromOtherFile();
const shouldSayPending = await api1;
const shoudHaveData = await api2;
}
The first function doesn't need to be an async one then, you just return a number of promises.
You could also consider async generators, which would give you a nicer code in the first method, but less nice in the second, like this:
async function* myPromise() {
try {
yield await callToAnApi(); // we need to await here so that
yield callToAnotherApi(); // this method is executed after.
} catch(e) {
// error handling
}
}
The other side would result in something like this:
async myPromise2() {
const progress = theIteratorFromOtherFile(); // just call the function*
const shouldBePending = (await progress.next()).value;
const theOtherResult = (await progress.next()).value;
}
Performance wise there's very little difference between the two - you're doing async operations so these are your bottlenecks. The choice is then up to your personal preference.

How to return Papa-parsed CSV via promise/async-await

Can someone help me understand why this returns a pending promise, rather than the data?
async function toJson (filepath) {
const file = fs.createReadStream(filepath)
let json = new Promise((resolve, reject) => {
Papa.parse(file, {
header: true,
complete (results, file) {
resolve(results)
},
error (err, file) {
reject(err)
}
})
})
let result = await json
return result.data
}
If I change the return result.data line to console.log(result.data), it logs the data array to the console, as expected. Why won't it simply return that array?!?!
As Roamer-1888 added in the comments, async functions always return a Promise, even if you await inside it and then return the data it will return as a Promise.
In the function's caller, you will have to await the Promise or use .then() on it in order to access the delivered data.
The toJson function could be better written to just return a Promise like this
function toJson (filepath) {
const file = fs.createReadStream(filepath)
return new Promise((resolve, reject) => {
Papa.parse(file, {
header: true,
complete (results, file) {
resolve(results.data)
},
error (err, file) {
reject(err)
}
})
})
}
Now when you call toJson(), you can use either await if you are inside of an async function or chain .then() on the returned Promise to access the data.
async function main() {
try {
const data = await toJson(filepath)
// do something with the data...
} catch (err) {
console.error('Could not parse json', err)
}
}
Or with .then()
toJson('path')
.then(console.log)
.catch(console.log)
You will be able to catch errors from the underlaying FileReader (thanks to calling reject inside the error function). Keep in mind that by calling resolve with results.data you put aside results.errors and results.meta which contain useful information about the read csv.

What are the down sides to wrapping promises in an object that resolves them?

I'm working on a new framework of microservices built in Node 8 and trying to simplify some of the logic required for passing Promises around between services.
I have a function I import in each service called StandardPromise which you can pass a Promise to. StandardPromise will call .then() on the promise and place the result in an object. If the promise was resolved it will be placed in the data attribute, if was rejected or threw an error then that will go in the err attribute.
The result of the above is that when a service receives a standardized promise by awaiting a call to another service, it can just check if there's anything in err and move forward with data if err is empty. This flow is significantly simpler than having .then() and .catch() blocks in every function.
I'm pretty happy with how it's turning out, and it seems to be working great, but since I haven't seen many examples of this kind of flow I want to know if there's something I'm missing that makes this a terrible idea or an antipattern or anything like that.
Here's a simplified, somewhat pseudocode example:
Service1:
const sp = require('./standardPromise');
const rp = require('request-promise-native');
function ex() {
// Wrap the Promise returned from rp as a "standardPromise"
return sp(rp.get({url: 'https://example.com'}));
}
Service2:
const Service1 = require('./Service1');
async function ex2() {
var res = await Service1.ex();
if (res.err) {
// Do error stuff
console.error(res.err);
return;
}
// Here we know res.data is our clean data
// Do whatever with res.data
return res.data;
}
standardPromise:
module.exports = function(promise) {
try {
return promise.then((data) => {
return {err: undefined, data: data};
}).catch((err) => {
return Promise.resolve({err: err, data: undefined});
});
} catch(err) {
console.error('promise_resolution_error', err);
return Promise.resolve({err: err, data: undefined});
}
}
It can just check if there's anything in err and move forward with data if err is empty. This flow is significantly simpler than having .then() and .catch() blocks in every function.
No, this is much more complicated, as you always have to check for your err. The point of promises is to not have .catch() blocks in every function, as most functions do not deal with errors. This is a significant advantage over the old nodeback pattern.
You would drop your standardPromise stuff and just write
// Service1:
const rp = require('request-promise-native');
function ex() {
return rp.get({url: 'https://example.com'});
}
// Service2:
const Service1 = require('./Service1');
async function ex2() {
try {
var data = await Service1.ex();
} catch(err) {
// Do error stuff
console.error(err);
return;
}
// Here we know data is our clean data
// Do whatever with data
return data;
}
or actually simpler with then for handling errors:
// Service2:
const Service1 = require('./Service1');
function ex2() {
return Service1.ex().then(data => {
// Here we know data is our clean data
// Do whatever with data
return data;
}, err => {
// Do error stuff
console.error(err);
});
}

How to return value from a promise that's inside another function?

I know that a lot of questions have been asked about async programming and Promises, but I really need an example with this specific code to figure out how I should go about returning promises that's being returned in another function.
I have two functions. The first one is called upon GET'ing to a route. This route should create a payment link and save a booking to a database.
exports.create_booking = function(req, res) {
req.body.payment = exports.create_booking_payment(req.body.total_amount);
console.log(req.body.payment); // This returns ' Promise { <pending> } '
var new_booking = new Booking(req.body);
new_booking.save(function(err, booking) {
if (err)
res.send(err);
res.json(booking);
});
};
However creating the payment link happens with an asynchronous method. My first problem was that I could only access the payment inside the methods callback function.
Now I have wrapped the method inside another (async) method in which a Promise is created and resolved. This method is being returned to my first method with an await statement, but all this returns is: ' Promise { } '.
I know that this happens because the method is being returned before the promise is resolved. But I don't understand why this is. My assumption is that the 'await' statement makes sure to wait returning the method before the async function is completed.
exports.create_booking_payment = async function() {
function asyncPayment() {
return new Promise (function(resolve, reject) {
mollie.payments.create({
amount: 20.00,
description: "Reservation code: ",
redirectUrl: "https://www.website.com/",
webhookUrl: ""
}, function(payment) {
if (payment.error) reject(payment.error)
else { resolve({
id: payment.id,
link: payment.getPaymentUrl(),
status: payment.status
})
}
});
});
}
return await asyncPayment();
}
I hope someone can help me out here...
You seem to have missed that an async function still returns a promise, not the actual value. So, when you call create_booking_payment(), you are getting back a promise that you need to use either .then() with or await with. There's no free lunch across a function boundary. await allows you to program in a synchronous-like fashion inside a function, but still does not allow you to return the value from the function. When it looks like you're returning the value from the async function, you're actually returning a promise that resolves to that value.
So, you'd do this with async and await:
exports.create_booking = async function(req, res) {
try{
req.body.payment = await exports.create_booking_payment(req.body.total_amount);
console.log(req.body.payment);
var new_booking = new Booking(req.body);
new_booking.save(function(err, booking) {
if (err)
res.status(500).send(err);
else
res.json(booking);
});
} catch(e) {
res.status(500).send(err);
}
};
or this with .then():
exports.create_booking = function(req, res) {
exports.create_booking_payment(req.body.total_amount).then(payment => {
console.log(payment);
req.body.payment = payment;
var new_booking = new Booking(req.body);
new_booking.save(function(err, booking) {
if (err)
res.status(500).send(err);
else
res.json(booking);
});
}).catch(err => {
res.status(500).send(err);
});
};
Note, I also added more complete error handling to both scenarios. Also, this code would be a lot cleaner if you "promisfied" or used an already promisified interface for your .save() method. I strongly dislike using plain callback async code inside of promise-based code because it ends up duplicating error handling (like you see in this case).
Also, create_booking_payment() doesn't need to be async or use await since all you need it to do is to return your promise which it already knows how to do:
exports.create_booking_payment = function() {
return new Promise (function(resolve, reject) {
mollie.payments.create({
amount: 20.00,
description: "Reservation code: ",
redirectUrl: "https://www.website.com/",
webhookUrl: ""
}, function(payment) {
if (payment.error) reject(payment.error)
else { resolve({
id: payment.id,
link: payment.getPaymentUrl(),
status: payment.status
})
}
});
});
}

Categories

Resources