await returns [Function] instead of value
Trying to return the values of a query from firebase by making use of the async and await function. Results returned are either [Function] or Unhandled Promise Rejection Warnings. New to promises, async and await but I've tried the basic examples on a few websites, most having the resolve and reject parameters, which I assume should be the same as the firebase promises.
I tried 2 different version, both with incorrect results.
get_all_users:async function(){
ref = db.ref("/user_data");
const value = await ref.orderByKey().on("child_added", function(data){});
console.log(value.val().name);
}
returns UnhandledPromiseRejectionWarning
function get_async_users(){
ref = db.ref("/user_data");
ref.orderByKey().on("child_added", function(data) {
return data.val().name;
});
}
returns undefined
Expected either one to return the names from the query.
on is for listening to all events that ever get triggered. Therefore it does not return a Promise, as a Promise represents a single value being available, and as it does not return a Promise, you can't await it (or at least: it doesn't make sense to do so, its a no-op).
If you want to wait for the first occurence, there is once, which also returns a promise that can be awaited.
Concerning the UnhandledPromiseRejection: You should always wrap your asynchronous code into a try / catch (inside an async function), or attach a .catch to the upmost promise in the chain, then you can handle those errors correctly.
Related
I have the following endpoint in a class called UserApi.js:
const controller = 'User';
...
export async function getEmployeeInfo(employeeId)
{
const query = createQueryFromObject({employeId});
const response = await get(`/${controller}/EmployeeInfo?${query}`);
return retrieveResponseData(response, []);
}
This is going to get the required information from an action method in the backend of UserController.cs.
Now, say that I want to display this information in EmployeeView.vue class, do I have to await it again? Why or why not? Initially, I would say no, you don't, as you already dealt with the await/async in the UserApi.js class, but what about the Promise.resolve? Please explain.
methods: {
async setReportData(
employeeId
) {
this.isBusy = true;
Promise.resolve(getEmployeeInfo(
employeeId
)).then((resultsEmployeeInfo) => {
this.reportDatatableEmployeeInfo = resultsEmployeeInfo;
})
.catch(() => {
this.alerts.error('An error has occurred while fetching the data');
})
.finally(() => {
this.isBusy = false;
});
},
Update:
....
* #param {Object} response
* #param {any} defaultData
* #param {Function} predicate
* #returns {Promise}
*/
export function retrieveResponseData(response, defaultData = null, predicate = (predicateResponse) => predicateResponse) {
const data = predicate(response) ? response.data : null;
return data || defaultData;
}
You need to await it since a function declared with async keyword ALWAYS returns a Promise, even if you do only synchronous stuff inside of it, hence you need to await or "thenize" it to access the value it resolved to. That depends from the implementation details of async functions which are just generators that yield promises.
If this concerns you because you work inside sync modules and don't like to use async functions just to execute more async functions, there's a good news, TOP-LEVEL await MODULES proposal is at stage 4, so it'll very soon be shipped with the next ECMA version. This way you will be able to await inside modules as if they were wrapped by async functions !
https://github.com/tc39/proposal-top-level-await
I can't tell if you need to await it again, because I can't tell what retrieveResponseData does. It might take the resolved value and wrap it in a fresh promise, which would then require callers of getEmployeeInfo to await the result.
Here's the why:
A Promise is a wrapper around a value
await unwraps a Promise. So does the .then() handler you can register with a Promise (but the value is only unwrapped within the function you provide to .then()).
Just like a gift in the real world, once something has been unwrapped, you don't need to unwrap it again. However, very conveniently for us, you can still use await on a value that is not wrapped in a Promise, and it will just give you the value.
You can wrap any value in a Promise, like so:
let wrappedFive = Promise.resolve(5)
//> wrappedFive is a Promise that must be unwrapped to access the 5 inside it
// this does _exactly_ the same thing as the above
let wrappedFive = new Promise(resolve => {
resolve(5)
})
Sometimes you end up in a situation where you can't use await, because you're in a function that cannot be marked async. The lifecycle methods of front-end frameworks like React (and possibly Vue) are like that: the framework needs each lifecycle method to do its job and be done immediately. If you mark the lifecycle method as async, you can often prevent it from having the intended effect.
In cases like that, you often need to use chained .then() handlers, which is a little uglier, but it works:
componentDidMount() {
// this API call is triggered immediately by lifecycle,
// but it basically starts a separate thread -- the rest
// of this function does not wait for the call to finish
API.getUserInfo()
.then(userInfo => {
// this happens after the API call finishes, but
// componentDidMount has already finished, so what happens
// in here cannot affect that function
this.setState({ username: userInfo.username })
})
// this happens immediately after the API call is triggered,
// even if the call takes 30 seconds
return 5
}
Note that a Promise does not actually start a separate thread -- these all happen in the same thread that executes the lifecycle method, i.e. the browser's renderer thread. But if you think of the codepath that executes, a Promise that you don't wait for basically introduces a fork into that codepath: one path is followed immediately, and the other path is followed whenever the Promise resolves. Since browserland is pretty much a single-threaded context, it doesn't really hurt you to think of creating a Promise as spawning a separate thread. This is a nuance you can ignore until you are comfortable with asychronous patterns in JS.
Update: retrieveResponseData does not appear to return a Promise. I could be wrong, if predict returns a Promise, but if that were true, then the ternary would always return response.data because unwrapped Promises are truthy values (even Promise.resolve(false) is truthy).
However, anyone who calls getEmployeeInfo will have to wait it, because that function is marked async, and that means it returns a Promise even if nothing inside it is is asynchronous. Consider this extreme example:
// this function returns a number
function gimmeFive() {
return 5
}
// this function returns a Promise wrapped around a number
async function gimmeFive() {
return 5
}
Async function getEmployeeInfo awaits the result of the get call in order to return the value returned by a call to retrieveResponeData.
Assuming neither get nor retrieveResponeData errors, the value syntactically returned in the body of getEmployeeInfo is used to resolve the promise object actually returned by calling getEmployeeInfo.
Promise.resolve is not needed to convert the result of calling getEmployeeInfo into a promise because, given async functions return promises, it already is.
It doesn't matter if retrieveResponseData returns a promise or not: standard async function processing waits for a returned promise value to be settled before resolving the promise returned when calling the async function.
Async function setRreportData is declared as async but written using chained promise handler methods to process data and error conditions in order - the async declaration could be omitted, but may be useful if modifications are made.
The results can only be used to update the page at a time when the data has finished being obtained and extracted, shown as a comment in the following code:
setReportData( employeeId) {
this.isBusy = true;
getEmployeeInfo(
employeeId
).then((resultsEmployeeInfo) => {
this.reportDatatableEmployeeInfo = resultsEmployeeInfo;
// At this point the result in this.reportDatatableEmployeeInfo can be used to update the page
})
.catch(() => {
this.alerts.error('An error has occurred while fetching the data');
})
.finally(() => {
this.isBusy = false;
});
},
Displaying the data using EmployeeView.vue class must wait until the data is available. The simplest place to insert updating the page (in the posted code) is within the then handler function inside setReportData.
Displaying the data
As posted setReportData does not notify its caller of when report data is available, either by means of a callback or by returning a promise. All it does is save the result in this.reportDatatableEmployeeInfo and dies.
Without using callbacks, setReportData is left with two choices
Return a promise that is fulfilled with, say,resultsEmployeeInfo or undefined if an error occurs:
setReportData( employeeId) {
this.isBusy = true;
// return the final promise:
return getEmployeeInfo(
employeeId
)
.then( (resultsEmployeeInfo) =>
(this.reportDatatableEmployeeInfo = resultsEmployeeInfo)
)
.catch(() => {
this.alerts.error('An error has occurred while fetching the data');
// return undefined
})
.finally(() => {
this.isBusy = false;
});
},
which could be used in a calling sequence using promises similar to
if(!this.busy) {
this.setReportData(someId).then( data => {
if( data) {
// update page
}
}
If you wanted to make the call in an async context you could use await:
if(!this.busy) {
const data = await this.setReportData(someId);
if( data) {
// update page
}
}
Update the page from within setReportData after the data becomes available ( as shown as a comment in the first part of this answer). The method should probably be renamed from setReportData to getReportData or similar to reflect its purpose.
This question already has answers here:
When should I use a return statement in ES6 arrow functions
(6 answers)
Closed 27 days ago.
I just wonder if there is different between those 2 calls by await Promise.all([/* ... /*])
In my performance test, it took the same time.
This is my method to be invoked by the Promise.all:
const makeFetch1 = async () => {
axios.get("https://jsonplaceholder.typicode.com/todos/1");
};
const makeFetch2 = async () => {
return axios.get("https://jsonplaceholder.typicode.com/todos/1");
};
1) await Promise.all([makeFetch1(), makeFetch1(), ...])
2) await Promise.all([makeFetch2(), makeFetch2(), ...])
Both have the same execution time result, what i should choose?
The first one will fire of a request and not return it. so makeFetch1() will result in a Promise that resolves immediately. This is almost certainly never what you want. An async function should await or return inside somewhere, otherwise it may as well be a standard function (that returns nothing).
Even if you don't care about what the request resolves to, you should still use the result of the request (by returning or awaiting, just so you can see if the request succeeds or not - and communicate to the user that there was a problem if it fails)
The second one will fire of a request and return it. so makeFetch1() will result in a Promise that resolves once the request resolves - which is likely to take a number of milliseconds.
There are several major differences and multiple coding mistakes.
First off this code:
const makeFetch1 = async () => {
axios.get("https://jsonplaceholder.typicode.com/todos/1");
};
is an async function that calls axios.get() and then immediately returns a promise that is already resolved and has an undefined resolved value. Nothing here waits for the axios.get() call to finish.
Your second code block:
const makeFetch2 = async () => {
return axios.get("https://jsonplaceholder.typicode.com/todos/1");
};
calls axios.get() and returns the promise that it creates. So, the promise this function returns will be tied to when the axios.get() call completes and the resolved value will be the resolved value of the axios call.
Differences:
You could use the makeFetch2() function to get the result of the axios.get() call (resolved value or reject error). You could not use makeFetch1() to get the result of the axios.get() call because that result is not communicated back to the caller in any way.
Similarly, makeFetch2() will allow the caller to know when the axios operation has completed. makeFetch1() will not.
If the axios.get() operation rejected, then makeFetch1() would result in an unhandled rejection error because nothing is listening for the rejection.
Then, these two:
await Promise.all([makeFetch1, makeFetch1, ...])
await Promise.all([makeFetch2, makeFetch2, ...])
Are both pretty useless because you're not calling any of the functions so nothing is executing. These will both just resolve immediately with a resolved value of an array of function references. None of the axios calls are executed because nothing actually calls makeFetch1() or makeFetch2().
If you meant to actually execute the functions with these two:
await Promise.all([makeFetch1(), makeFetch1(), ...])
await Promise.all([makeFetch2(), makeFetch2(), ...])
Then, the first one just resolves immediately with an array of undefined values as the resolved value because remember the makeFetch1() return value isn't connected in any way to the axios.get() call.
The second one will properly wait for all the axios.get() calls in makeFetch2() to complete and will resolve with an array of values from the axios.get() calls.
Both have the same execution time result, what i should choose?
That's because as you've implemented them, you aren't calling the makeFetchN() functions in either case. So, they aren't doing anything and thus have the same execution time.
If you change the implementation to actually call the functions as in:
await Promise.all([makeFetch1(), makeFetch1(), ...])
await Promise.all([makeFetch2(), makeFetch2(), ...])
There will be a significant timing difference because the one based on makeFetch1() does not wait for any of the axios.get() calls to finish and does not communicate back results or completion and is subject to uncaught rejections.
The one based on makeFetch1() has no practical use as you could just do:
makeFetch1();
makeFetch1();
...
with the exact same result because makeFetch1() just returns an immediately resolved promise so passing them all to Promise.all() isn't doing anything useful.
makeFetch1() is probably just not useful since it doesn't inform the caller of completion, error or resolved value.
I use pg-promise and i tried to make a request to database in user model to get an array of all users like this:
exports.getAllUsers = function () {
let users = db.any("SELECT * FROM users;").then(function (data) {
return data; // in debug mode data is my expected array
});
return users;
};
But when i make get request from controller, it returns promise Promise { <pending> }, not an array of json objects that I expected.
There is a controller:
exports.getUsers = function (req, res) {
let users = userModel.getAllUsers();
console.log(users); // here it outputs Promise { <pending> }
res.json(users);
};
How to get array instead of promise?
Let's break down your code. You are exporting a function of your design, getUsers, which is supposed to return a sequence of user objects. But it does not -- db.any(...) returns a Promise. How else do you think you can call then on the returned object? then is a member function of the Promise class, and you pass another function to then to work with the actual data (passed as parameter to the function you pass to then). Meaning that indeed data, as passed to the then call invoked on the return value of db.any(...) call, is the sequence of users you're after.
The mistake you are making is to assume that if you return data from the callback passed to then, it will become the return value of the then(...) call. It will not. then always returns a Promise -- whatever you return from the callback to then will become the resolved value of the returned promise, but it is the promise that is returned and that is why your users variable is a Promise.
You need to read more about promises, and how they are resolved asynchronously (between running your scripts), and how to synchronise your code on them resolving.
Hint: Use the await keyword to wait for and use the value given a promise, like the value you return from the callback that you pass to your then(...) call, or accept the design of your getUsers function returning a promise and adapt the rest of your code to work with that, without use of await.
Your getAllUsers function can be reduced to:
exports.getAllUsers = function () {
return db.any("SELECT * FROM users;");
}
...and with await you can then use it like:
let users = await getUsers();
The statement above has to be part of a function that is tagged as async though -- ECMAScript requires marking these explicitly in order to allow use of await expressions:
async function whatever() {
let users = await getUsers();
/// ...
}
Execution of a script that invokes an async function such as one containing the statement above, will be interrupted by the JavaScript interpreter at each await expression, and resumed when the promise expressed after the await keyword, resolves to a value, with the value being assigned to the users variable.
Using async and await does require a version of Node.js that can support them, but current release does (as of the time of writing this).
Otherwise you can still keep the getUsers function, but you need to use it differently since you can't use await -- having to use promises as one did prior to introduction of await and async, like:
getUsers().then(users => {
/// Do something with the `users` array.
});
At any rate, it looks like you have some holes in your understanding of how promises work, and I recommend you fill these holes by reading about them this time and not just going straight through to the pg-promise API, which builds on them.
Like you already found out userModel.getAllUsers() will return a promise and not an array. You need to wait for this promise to be resolved. This can be done using the an async function.
exports.getUsers = async function (req, res) {
let users = await userModel.getAllUsers();
console.log(users);
res.json(users);
};
Note that an async function will always return a promise.
Or you can make use of then method of the returned promise.
exports.getUsers = function (req, res) {
userModel.getAllUsers().then(users => {
console.log(users);
res.json(users);
});
};
Supposed I have a json object from a url.
fetch(URL, setting)
.then(loadRes)
.then(logger)
function loadRes (res){
return res.json();
}
Not sure whether async is needed right below.
async function logger (reply){
let stringReply = await reply.answer;
console.log("Obtain value " + stringReply) //let the value be a string e.g "apple"
sendingReply(stringReply);
return stringReply
}
Using express
app.post('/', function( req, res){
console.log(sendingReply())
}
when i tried logging the results again, it shows promise { <pending> }
So how can I retrieved the value from the earlier on. Waiting for the value to be retrieved
Correct me if I'm wrong, my understanding is that when the code is being run, it execute everything at once. So therefore by the time I received a result from the URL link (json object), it is already done executing it.
If you are sure that reply.answer is a resolved promise, then only await would be required. Since you've mentioned it is returning Promise { <pending> }, I guess it would be an unresolved Promise which gives a pending state.
x=new Promise((resolve,reject)=>("vre")) //gives PromiseĀ {<pending>}
x=new Promise((resolve,reject)=>resolve("vre")) //gives PromiseĀ {<resolved>: "vre"}
I hope that resolves your concern.
If you're using await then async function is required and in this case correctly used. However, await has to be used before a promise call.
What does reply.answer return? if it returns a promise then reply.answer() is the correct way to call that function.
Please let know your output error message, also let me know what's the purpose on sendingReply(), it's supposed to return a promise? does it return a value?
find is a function that returns a promise that resolves to a value, but also has data on the promise object itself. A practical use case is when find() returns a dbQueryObject that can be used elsewhere, whereas await find() resolves with databaseResults. eg:
function find () {
var results = new Promise(/* resolves with databaseResults */)
Object.assign(results, new dbQueryClass)
return results
}
I can wrap find with a function to provide helpers like this,
function findPage (page) {
return find().skip(page*20).limit(20)
}
and use it like findPage().select('_id') to get the query object or await findPage().select('_id') to get the resolved value (returns something similar to find()).
However, if I wrap find with a promise like this,
async function findSomething() {
var someArgs = await promise1()
if (someArgs) return find(someArgs)
return find()
}
How do I get the value of find() itself outside of findSomething? I need promise1 to resolve, but I need find() to not resolve. find() is a plain object AND a thenable, depending on if you resolve it or not, but resolving findSomething() will automatically resolve the returned value. How do I return it such that it doesn't resolve?
My constraint is that I cannot modify the library that provides find() to not return a thenable object.
I don't see what would "usually" be an antipattern here. You can return or await any promise, thenable or plain value in an async function, just as you can Promise.resolve them or return them from a then callback. There is nothing unusual about this, that's just how it is done.
If you want to be explicit about returning a promise, instead of relying on thenable assimilation, you can always call .exec() on your mongoose query and it will get you a "real" promise.
As stated here https://stackoverflow.com/a/22724984/704894, it's impossible to have Promise<Promise<value>> because when you return a Promise inside .then handler, it gets automatically resolved (unwrapped).
You'd have to use Promise<Wrapper<Promise>> to bypass this limitation.
Sidenote for functional programming folks: .then is both map and flatMap in Haskell terms.