Why does await function return pending promise - node.js

let request = require('request-promise')
function get(url) {
let _opt = {}
let response = (async () => {
try {
var ret = await request(url, _opt);
return ret;
} catch (e) {
console.log(e)
}
})();
return response
}
console.log(get('http://www.httpbin.org/ip'))
gives:
Promise { <pending> }
Why doesn't it wait for my response?

Why doesn't it wait for my response?
That's simple, because you are returning a promise. Node js is single thread and is executed in a non blocking way.
That means that return response in your get function is executed before the resolution of response variable.
Try as follow:
let request = require('request-promise')
function get(url) {
let _opt = {}
return request(url, _opt);
}
async function some () {
console.log(await get('http://www.httpbin.org/ip'));
}
some();
This example is also returning a promise, but in this case we are awaiting for the promise resolution.
Hope this help.

Async functions are non-blocking and will immediately return a promise rather than waiting for a result. This for example, allows for multiple async functions to run concurrently rather than each running sequentially.
To wait for a result, you can use the await keyword to block until the promise is resolved. The await command will return the result of the promise. For example, to log the result of the get function, you can change the last line to:
console.log(await get('http://www.httpbin.org/ip'))
UPDATE:
Let me give this one more try (sorry if I'm beating a dead horse here).
Since your response variable is an async function, it's return type is a promise by the definition of an async function. Even though the ret variable is await'ed, that just means that it will block while writing to the ret variable. The response async function needs to be run to completion in order for the await to complete. An await here would be useful if you wanted to wait for the return value and then add post-processing, but in this example specifically, your try block could simply be:
try {
return request(url, _opt)
}
Since request already returns a promise, the extra await is causing a trivial amount of overhead since it implicitly creates an extra promise.
Since response is a promise, and you are returning response, the function get is also returning a promise. You are then trying to log this promise (which obviously doesn't work). This is why you must await or .then the get function to get the result.
Source: https://medium.com/#bluepnume/learn-about-promises-before-you-start-using-async-await-eb148164a9c8 under "Pitfall 1: not awaiting"

Related

How the while loop works with promise and callback function in Nodejs?

This is my first time to write while loop with Promise and callback. I don't know why it leads to infinite loop. How to fix it?
async function getResult(){
return new Promise((resolve, reject) => {
let params ="some input and setting";
let next = "hasNext";
let array = [];
let error = null;
while(next !== null){
checkNext(params, function(err,data) { //checkNext is a function to return the current list and check wether there has the next list
if(err){
next = null;
error = err;
}else{
next = data.hasNext; // if there is not next list, data.hasNext = null
array = array.concat(data.array); // data.array return the current list
}
});
}
if(error !== null){
reject(error);
}else{
resolve(array); // I want to return all lists
}
});
}
It leads to an infinite loop because checkNext() is asynchronous and non-blocking so your while() just runs forever before even one call to checkNext() gets a chance to finish and call its callback.
You never use a while() loop waiting for some asynchronous things to finish in Javsacript (except with await as shown below) because with the event driven architecture of nodejs, the while loop never returns control back to the event loop so no asynchronous operation can never get its completion event processed and thus the thing you are waiting for never gets a chance to happen. Things are different (as shown below) if you use await to await a promise that is connected to your asynchronous event. Then, you can use a while() loop successfully.
With asynchronous operations where you're going to use promises, you pretty much always want to promisify your asynchronous operations so all your control flow is with promises, not plain callbacks as the two don't mix very well. This is what I would suggest:
const { promisify } = require('util');
const checkNextP = promisify(checkNext);
async function getResult() {
let params = "some input and setting";
let next = "hasNext";
let array = [];
while (next !== null) {
let data = await checkNextP(params);
next = data.hasNext; // if there is not next list, data.hasNext = null
array = array.concat(data.array); // data.array return the current list
}
return array;
}
Here, the while loop works because we're using await with a promise returned from checkNextP() and that await suspends the execution of the function until that promise resolves/rejects.
A little more explanation about how async functions work
At the point we hit the first await this async function will automatically return a promise. The caller will get that promise at that point. Then, when the promise with that first await resolves, the function will resume, you will get the first data value and the rest of your loop executes. This process will repeat until next is null. At that point, your while() loop will be done and the return array statement will execute. Because this is an async function, what that return statement really does is it resolves the promise that the async function previously returned and sets the array to be the resolved value of that promise.
If, the promise from checkNextP() rejects, then the await checkNextP() will throw the rejection and since we don't have a try/catch around it, the async function will automatically catch that throw and it will reject the promise that the async function has previously returned, causing the caller to get a promise rejection with whatever error checkNextP() rejected with. So, the error handling here works too.
The caller of getResult(), just needs to do something like this:
getResult().then(results => {
console.log(results);
}).catch(err => {
console.log(err);
});
Or, the caller could also be in an async function itself and use await and try/catch to catch errors.

Returning promise values with `then`

In the below code snippet I am using then to get the result of a promise. However, response is not returned. What is returned is Promise { <pending> }.
I've logged response and can see it correctly returned the data, not a pending promise. So why is it returning a pending promise? I've even added a then call to the call to describeTable, and it still comes back pending.
I've read the following questions and they were not helpful, so please do not mark as a duplicate of them:
How do I return the response from an asynchronous call?
async/await implicitly returns promise?
const AWS = require('aws-sdk');
AWS.config.update({region: 'eu-west-2'});
const docClient = new AWS.DynamoDB;
async function describeTable() {
const params = {
TableName: 'weatherstation_test',
};
let response;
try {
response = await docClient.describeTable(params).promise().then(result => result);
console.log(response); // Logs the response data
} catch (e) {
console.error(e)
throw e;
}
return response;
}
console.log(describeTable().then(result => result)); // Logs Promise { <pending> }
Update
So I've removed the first then (after promise()) because it is redundant. The answer from #libik works for me. It was the context in which then is run I wasn't understanding.
You cannot access the asynchronous content from synchronous one.
If you want to log it, you have to do it from inside like this
describeTable().then(result => console.log(result))
In your case you are logging the output of asynchronous function, which is always a promise.
What really happens: In Node.js all the synchronous content is executed first and any asynchronous content is put to event loop to be executed later.
So first this is executed
const AWS = require('aws-sdk');
AWS.config.update({region: 'eu-west-2'});
const docClient = new AWS.DynamoDB;
console.log(describeTable()
The function is called, so then it goes inside function. Because it is asynchronous function, it will execute it synchronously till the first await.
const params = {
TableName: 'weatherstation_test',
};
let response;
try {
response = await docClient.describeTable(params).promise()
Now this promise is added to Event Loop to be executed later and the function describeTable() returns to the synchronous context (the console log) the promise, that will be linked through all your function asynchronously later and you log this promise (which is indeed pending).
And now your synchronous context ends. Before the above promise is resolved your app can meanwhile executing other parts (which is always the same, take event from event loop, do the full synchronous part - which can push another events to event loop and then take another event and repeat)
In order to log the result you need to write the following:
describeTable().then(result => console.log(result));

Async Await Behaviour

A simple code to understand async/await is making me crazy.
I have a button on click on which i am reading some value from localstorage and showing it in an alert box. Before showing the alert i want to console.log the value.
If i understood async/await my code should d exactly that but it is working in reverse order. first the alert is coming and then the console.
//Method called on button click
findMyAge2() {
this.getData("age").then(result => {
console.log('Hi');
myAge2 = result;
});
alert(myAge2);
}
async getData(key): Promise<any> {
return await this.storage.get(`${key}`);
}
Expected Result:
in Console log :Hi
UI: Age
Actual Results:
UI: Age
in Console log :Hi
JavaScript is asynchronous by nature, so when you have a Promise returned, code continues execution and that Promise resolves some time afterward.
async/await is a way to control this flow of programming and can allow you to create Synchronous code that "awaits" the result of asynchronous execution.
Your problem here is that you want to return the promise from your getData function and await it in your findMyAge2 function.
async function findMyAge2() {
let result = await this.getData("age");
console.log('Hi');
myAge2 = result;
alert(myAge2);
}
async getData(key): Promise<any> {
return this.storage.get(`${key}`);
}
By adding async keyword to a function it will always now return a Promise. So you do not need to await in your return statement. That effectively does nothing. It would be like saying:
function getData(key) {
return new Promise(resolve => {
return localStorage.get(`${key}`).then(result => resolve(result))
})
}
You don't need to await that result in local storage because the consumer should be calling await or .then on the result regardless.
Try this way,
findMyAge2() {
this.getData("age").then(result => {
console.log('Hi');
myAge2 = result;
alert(myAge2);
});
}
async getData(key): Promise<any> {
return await this.storage.get(`${key}`);
}
Before you should wait to alert before data being retrieved.
async/await works like with promises, which means, code gets executed and you don't know when it will finish, you just know that after if finishes, you want something else to be executed.
This is exactly what you put inside the "then" function, which is what gets executed after the 1st part (asyc getData) gets executed.
In the case of your code, the findMyAge2 gets called when you click the button, then it executes the getData and specifies what happens after getting the result from that call, within the "then" block.
Therefore you just have to move the alert, into "then" block, then you'll have the result you expect.
var myAge2;
findMyAge2() {
this.getData("age").then(result => {
console.log('Hi');
myAge2 = result;
alert(myAge2);
});
}
async getData(key): Promise<any> {
return await this.storage.get(`${key}`);
}

I have a function with chained promises after which I want to return an integer value but I get undefined before function is resolved

I have created a function that downloads a PDF then reads it and finds the number of pages with chained promises. I want to return the value of the pages when this function is called.
However when I write var b = await ExtractLength(url, destinationFolder,filename); in my main function then console.log(b) I am getting a value of undefined. If I understand correctly the variable b is assigned the value before the promises are resolved. I have tried some alternatives but I don't understand what the problem is. Why is this happening and how I can fix it?
Here is my function:
async function ExtractLength(url, destination, flnm){
var options ={
directory: destination,
filename: flnm
}
await download(url, options, async function(error){
if (error){
console.log("Download error: ", error);
return null;
}
await fs.readFile(destination+flnm, async function(err,dataBuffer){
if (err){
console.log("Read file error: ", err);
return null;
}
await pdf(dataBuffer).then(function(data){
console.log("Number of pages: ", data.numpages);
return data.numpages;
})
.catch(function(erro){
console.log("Pdf error: ",erro);
return null;
});
});
});
}
Your code here is a bit of an antipattern. You usually shouldn't be using both a callback and a Promise. JS async and await are used to get the result of a promise into a variable. For example const x = await Promise.resolve(5); // x = 5
I believe AWS does support both callbacks and promises so just choose one or the other. It looks like you have a good handle on callbacks so the simplest way would probably be removing any async and await keywords. Promises are definitely worth learning!
It would be easier to give a definite solution using the two approaches if you showed us where the download function comes from.
Change the following lines
await download(url, options, async function(error){
await fs.readFile(destination+flnm, async function(err,dataBuffer){
await pdf(dataBuffer).then(function(data){
to have returns like so
return await download(url, options, async function(error){
return await fs.readFile(destination+flnm, async function(err,dataBuffer)
return await pdf(dataBuffer).then(function(data){
You must return a value for your Extract, your readfile function, and your pdf function calls.
To further explain: in each of those chained calls, you're passing anonymous functions for them call, and even though each anonymous function returns a value, you never assign the final value to anything within the Extract call that kicks it all off.

Why I need to call every function with async keyword for calling an API in modularized code

I have just started with async and await and trying to convert all my callback syntax to async/await style.
One thing I could not understand is, why I need to every time prefix my function with async keyword.
Here is the example:
APIQuery.js
makeRequest: async(options) => {
try {
var response = await(request(options1))
}
catch(err){
console.log("Some error occurred");
response = undefined;
}
return response;
}
MobileAPI.js
getMobileData: async modal => {
var options = {method: 'GET', json: true,uri: 'https://example.com/mobile/'+modal}
var response = await APIQuery.makeRequest(options);
}
MobileService.js
getMobileDataService: async modal => {
var response = await MobileAPI.getMobileData(modal);
}
MobileController.js
Similarly again I have to use async and await combination to return response.
So my question is, is there a way to get rid of using this everywhere. Calling async await inside APIQuery.js is not enough?
If you want to use the await operator, you have to use async keyword before function declaration:
The await operator is used to wait for a Promise. It can only be used inside an async function.
If you don't want to use async everywhere, you can continue using callbacks or Promises (then, catch, etc.).
So my question is, is there a way to get rid of using this everywhere.
You can't block. Period. As soon as you block, your server would become completely unresponsive to all requests until it's unblocked. For this reason, your code must be asynchronous.
Callbacks are a perfectly valid form of asynchrony, but async/await is easier to use, understand, and maintain.

Resources