Async function returning Promise after await [duplicate] - javascript

I'm trying async/await functionality. I have such code imitating a request:
const getJSON = async () => {
const request = () => new Promise((resolve, reject) => (
setTimeout(() => resolve({ foo: 'bar'}), 2000)
));
const json = await request();
return json;
}
When I use the code in this way
console.log(getJSON()); // returns Promise
it returns a Promise
but when I call this line of code
getJSON().then(json => console.log(json)); // prints { foo: 'bar' }
it prints json as expected
Is it possible to use just code like console.log(getJSON())? What don't I understand?

Every async function returns a Promise object. The await statement operates on a Promise, waiting until the Promise resolves or rejects.
So no, you can't do console.log on the result of an async function directly, even if you use await. Using await will make your function wait and then return a Promise which resolves immediately, but it won't unwrap the Promise for you. You still need to unwrap the Promise returned by the async function, either using await or using .then().
When you use .then() instead of console.logging directly, the .then() method makes the result of the Promise available to you. But you can't get the result of the Promise from outside the Promise. That's part of the model of working with Promises.

A function defined with async always returns a Promise. If you return any other value that is not a Promise, it will be implicitly wrapped in a Promise. The statement const json = await request(); unwraps the Promise returned by request() to a plain object { foo: 'bar' }. This is then wrapped in a Promise before being returned from getJSON so a Promise is what you ultimately get when you call getJSON(). So to unwrap it, you can either call getJSON().then() like you've done or do await getJSON() to get the resolved value.

Return value of an async function will always be an AsyncFunction Object, which will return a Promise when called. You can not change that return type. The point of async/await is to easily wait for other async process to complete inside an async function.

const getResOrErr = () => {
const callAsyncCodeHere = async () => {
const request = () =>
new Promise((resolve, reject) =>
setTimeout(() => resolve({ foo: "bar" }), 2000)
);
const json = await request();
return json;
};
return callAsyncCodeHere()
.then(console.log)
.catch(console.log);
};
getResOrErr();
Try this. You can achieve making a function inside your main function and then put you promise code inside that function. Call it there and when you get the response or error just return it.

Related

Why objects key still has unknow promise , though I have used await for mapping

I'm new to javascript. as I know using array.map over async function returns array of promises.
and I can use await Promise.all to reolve all promise and it returns me data.
I want to understand how can I use asyn function inside array of object's key.
As I know using async function it never block as execution for unimportant line, ( here other execution is not dependent on url , so I'm trying to make it asynchronous)
async function showPost() {
const posts = [
{ title: 'a', url: ['q', 'o'] },
{ title: 'b', url: ['t', 'y'] },
];
const formattedPost = await formatPosts(posts);
console.log(formattedPost);
}
const formatPosts = async (posts) => {
const formattedPosts = await Promise.all( // NOTE: await promise.all on map
posts.map(async (post) => {
post.url = addUrl(post.url); //NOTE: here if I don' add await here post.url is Promise<Unknown>
return post;
})
);
return formattedPosts;
};
const addUrl = async (d) => {
const mm = await Promise.all(d.map((item) => item + '-dummyUrl'));
return mm;
};
showPost();
**CODE 1 without await , but inside await Promise.all **
CODE 2 with AWAIT on addURL call I get output
Why is it not resolving though it is inside await promise.all
thanks for help in advance
When you call Promise.all(), you're resolving the promises returned by the map() function. When you don't add await to addURL(), you're replacing that value in the object with a promise object, which isn't resolved by Promise.all().
Promises are still promises, even after they resolve.
On top of this, Array.prototype.map() isn't an async function, so addURL() doesn't need to be async, which makes adding await pointless.
I know using array.map over async function returns array of promises, and I can use await Promise.all to resolve all promise and it returns me data.
This will just wait for the promises that are elements of the array that was passed to Promise.all, not "all promises" anywhere.
Notice that without the await, you don't need to make the map callback an async function, and you don't need to wait for the promises in the mapped array then; your current code is exactly equivalent to
const formatPosts = async (posts) => {
const formattedPosts = posts.map((post) => {
post.url = addUrl(post.url);
return post;
});
return formattedPosts;
};
Why objects key still has unknown promise
Because you assigned a promise object to the object property. Never did you instruct anything to wait for that promise. This is what would be fixed by doing
post.url = await addUrl(post.url);

Why is my async function returning a pending promise after await?

I must be missing something here because I do not understand why my promise is not resolving.
I boiled the code down to this simple example:
...
console.log("before");
const promise = second();
console.log("after");
console.log(promise);
...
async function first() {
const p1 = await axios.get("https://<url>");
console.log("first");
console.log(p1.data);
return (p1);
}
async function second() {
const p2 = await first();
console.log("second");
console.log(p2.data);
return (p2);
};
Which produces this output in the console:
before
after
Promise { <pending> }
first
p1.data
second
p2.data
My understanding is that await pauses execution until it completes before moving to the next line, but that is clearly not happening.
The initial block completes with a pending promise, and then the awaited code in the async functions executes instead of pausing.
What am I missing to make my code pause and wait until the await lines complete with a resolved promise before continuing?
In JavaScript, all of the following are true:
A promise
Is a value (can be passed around to functions, assigned to variables, and so on)
Can be in the resolved, rejected, or pending states
The thing it resolves to is also a value (1, "foo", {}, ...)
Can have .then(...) callbacks chained to it
Can be awaited
An async function
Is a function that synchronously returns a promise
Is a function that asynchronously returns the resolved value of that promise
Hopefully this modified example will make it clearer:
// mock axios
const axios = {
get: url => Promise.resolve({ data: `Some data from ${url}` })
}
const fetchMockData = async () => {
const val = await axios.get('https://<url>')
return val
}
const withAsyncAwait = async () => {
console.log('=== with async/await ===')
const promise = fetchMockData() // not awaited
console.log('type of promise:', promise.constructor.name)
const resolved = await promise // here we await it
console.log('type of resolved:', resolved.constructor.name)
console.log('data:', resolved.data)
}
// equivalent but with `then` callback
const withThenCallback = () => {
console.log('=== with then callback ===')
const promise = fetchMockData() // not awaited
console.log('type of promise:', promise.constructor.name)
promise.then(resolved => { // resolved thanks to `then`
console.log('type of resolved:', resolved.constructor.name)
console.log('data:', resolved.data)
})
}
setTimeout(withAsyncAwait, 0)
setTimeout(withThenCallback, 100)
async function is not actually an synchronouse function like we think of, its an promise. So you need to handle it like an promise like:
console.log("before");
second().then(res => {
console.log("after");
console.log(res);
});
An async function is like a Promise which does not resolve immediately. So you should probably use an immediately invoked anonymous async function:
(async()=>{
console.log("before");
const promise = await second();
console.log("after");
console.log(promise);
})();

Does client-side code that calls an an async function need to use await?

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!

How to make `return` respecting async/await in JS?

I'm trying to make this code (a class method) returning a String.
async sign() {
const txt = 'ddddd';
const result = await crypto.createHash('md5').update(txt, 'binary').digest('hex');
return result;
}
The problem is that it ignores await and returns a Promise. This function's returned value is used as a HTTP request header, and while npmlog says it's
apisign: Promise { 'faaa3f1409977cbcd4ac50b5f7cd81ec' }
in network traffic caught by Wireshark I see
apisign: [object Promise]
How do I make return respecting await, or how should I write it so it returns a String?
You should not return the value of an async function as is, since it is a Promise
await for it before serializing it.
An async function always returns a Promise.
If you invoke sign() inside another function then you have to await for it and this requires making the caller function also an async function and so on and so forth.
Eventually, at the top-level code you have to use the regular .then().catch() syntax to wait for the Promise to settle:
sign()
.then((result) => {
// do something with the result (e.g. put it into the response header)
console.log(result);
})
.catch((err) => {
// something wrong happened and the Promise was rejected
// handle the error
console.log(`There was an error: ${err.message || err}`);
});
You will have to await the response of a async function.
const getURL = (title, page) => `https://jsonmock.hackerrank.com/api/movies/search/?Title=${title}&page=${page}`
const callService = async (title, page) => {
let response = await fetch(getURL(title, page));
return await response.json();
}
async function callApi() {
let data = await callService('spiderman', 1);
console.log(data.data.length);
}
callApi();

async/await always returns promise

I'm trying async/await functionality. I have such code imitating a request:
const getJSON = async () => {
const request = () => new Promise((resolve, reject) => (
setTimeout(() => resolve({ foo: 'bar'}), 2000)
));
const json = await request();
return json;
}
When I use the code in this way
console.log(getJSON()); // returns Promise
it returns a Promise
but when I call this line of code
getJSON().then(json => console.log(json)); // prints { foo: 'bar' }
it prints json as expected
Is it possible to use just code like console.log(getJSON())? What don't I understand?
Every async function returns a Promise object. The await statement operates on a Promise, waiting until the Promise resolves or rejects.
So no, you can't do console.log on the result of an async function directly, even if you use await. Using await will make your function wait and then return a Promise which resolves immediately, but it won't unwrap the Promise for you. You still need to unwrap the Promise returned by the async function, either using await or using .then().
When you use .then() instead of console.logging directly, the .then() method makes the result of the Promise available to you. But you can't get the result of the Promise from outside the Promise. That's part of the model of working with Promises.
A function defined with async always returns a Promise. If you return any other value that is not a Promise, it will be implicitly wrapped in a Promise. The statement const json = await request(); unwraps the Promise returned by request() to a plain object { foo: 'bar' }. This is then wrapped in a Promise before being returned from getJSON so a Promise is what you ultimately get when you call getJSON(). So to unwrap it, you can either call getJSON().then() like you've done or do await getJSON() to get the resolved value.
Return value of an async function will always be an AsyncFunction Object, which will return a Promise when called. You can not change that return type. The point of async/await is to easily wait for other async process to complete inside an async function.
const getResOrErr = () => {
const callAsyncCodeHere = async () => {
const request = () =>
new Promise((resolve, reject) =>
setTimeout(() => resolve({ foo: "bar" }), 2000)
);
const json = await request();
return json;
};
return callAsyncCodeHere()
.then(console.log)
.catch(console.log);
};
getResOrErr();
Try this. You can achieve making a function inside your main function and then put you promise code inside that function. Call it there and when you get the response or error just return it.

Categories

Resources