I am trying to get the owner variable and since the function usually returns it as undefined I decided to use async/await but I can't use await within another function and I can access the variable outside of it. Is there an easy solution or do I have to rework the function?
async getOwner() {
database.collection("users").doc(this.owner).get().then(data => {
var owner = new Promise(function (resolve, reject) {
resolve(new User(data.data().username, "", data.data().email, [], data.data().info))
})
let result = await owner
return result
})
}
If you're using async/await, there is almost never a need to use then() or new Promise(). You can write simply this, awaiting the promise returned by get():
async function getOwner() {
const snapshot = await database.collection("users").doc(this.owner).get()
const data = snapshot.data()
return new User(data.username, data.email, [], data.info)
}
You should probably also write code to check if there was any data in the snapshot and decide what to do if the document was not found as seen in the documentation.
You might want to review how async/await works in order to use it effectively.
https://javascript.info/async-await
While Doug's answer is the better solution (hence my upvote on it), I wanted to point out why your current approach doesn't work, which is because you're not returning anything from the top-level code.
So if you want to not use await, the closest to get your current code working would be:
function getOwner() {
// 👇 Add return here
return database.collection("users").doc(this.owner).get().then(data => {
var owner = new Promise(function (resolve, reject) {
resolve(new User(data.data().username, "", data.data().email, [], data.data().info))
})
let result = await owner
return result
})
}
And the simpler version, getting rid of the extra code:
function getOwner() {
return database.collection("users").doc(this.owner).get().then(doc => {
return new User(doc.data().username, "", doc.data().email, [], doc.data().info)
})
}
The top-level return is needed, because it allows the nested return to "bubble up" and back to the code that calls getOwner.
Related
There is some Advanced topics in JS I am learning and there is something I am trying to understand.
I've written a VueJS application and I need to expose some data and possibly methods from Vue outside of Vue itself. I am using vuex.
This is so that users can use normal JS on the front end to extend the application.
The idea is the user would add something like this on the front end. I have seen several JS frameworks/apps that do something like this.
AppName.onReady.then(function(instance) {
// use the instance object
var email = instance.data["email"]
var name = instance.data["name"]
var isComplete = member.isComplete
})
Now what I am trying to understand is the code I would need in my App to make the above work.
I know from the code it is a Class AppName that is invoked with new AppName({}) and it has a promise that gets resolved but not sure how to accomplish it.
Here is a sample for returning a promise.
const AppName = {
onReady() {
return new Promise( (resolve, reject) => {
// do your stuff here, then call resolve().
// sample follows
setTimeout(()=>{resolve()}, 1000)
})
}
}
Call it like this. Make sure to invoke onReady(), not just onReady.
AppName.onReady().then(()=>{
// your code here.
})
Here is one way you can can achieve that effect:
class AppName {
constructor(data = {
email: null,
name: null
}) {
this.data = data;
}
get onReady() {
return new Promise((resolve, reject) => {
resolve(this);
});
}
}
const handleOnReady = instance => {
const {
email,
name
} = instance.data;
console.log(`${name}'s email address is: ${email}.`);
};
const tom = {
email: 'tom#fakemail.com',
name: 'Tom'
};
const app = new AppName(tom);
app.onReady.then(handleOnReady);
You can mark a method as async. Then it returns a promise implicitly.
const AppName = {
async onReady() {
// do your stuff here, then simply return to resolve.
// throwing a exception calls reject.
}
}
Usage:
AppName.onReady().then(()=>{
// app is ready
})
If your client is in an async function, you can simply use await
Usage:
// must be inside async function
await AppName.onReady()
// app is ready
To use await at root level, wrap it in immediate executed function:
(async()=>{
await AppName.onReady()
})()
Ok so i've searched around and found nothing related to this problem.
My problem is something like this ->
I create an object (push into array) with some info taken from an api. After getting the info from the api i need to call yet another API to get further information on users. Since there are multiple keys for users i'd like to be able to set them inline with a simple function.
I'm doing something like this ->
_item.push({
Author: setPeople(item.Author.Title),
Title: item.Title,
....
Requester: setPeople(item.Requester.Title
})
At the moment i am getting the promise set(entirely) and not the PromiseValue. I know you usually do something like setPeople(name).then(() => {}) however that is not working in my object (sets the key too fast).
Any tip on how i should approach this?
Updating with more code.
export const retrieveIrfItems = async (spId) => {
let spQuery = "SITE" + SpQueryExtend1 + spQueryExpand;
return new Promise((resolve, reject) => {
let _items = [];
axiosApi.get(SiteUrl + spQuery).then((response) => {
//console.log(response.data.d);
return response.data.d;
}).then(async (item) => {
//let requesterSP = setPeople()
const createSPUser = async (user) => {
let spUser;
console.log("User prop is");
console.log(user);
setPeople(user).then((item) => {
spUser = item;
});
return spUser;
}
_item.push({
Author: setPeople(item.Author.Title),
Title: item.Title,
....
Requester: setPeople(item.Requester.Title
})
Ignore the unused function, i'm still doing tests to find a way for this problem.
Found the fix thanks to comments.
Using async/await wouldnt help me since i'd still get promise pending or undefined. What i had to use is ->
Requester: await setPeople(item.Requester.Title).then((user) => { return user }),
Using that in my object seems to work, but my question is...how good is this approach? If there are lots of fields with this behaviour (currently 5), wouldnt that slow down the page by...a lot ?
Then you should try something like that :
export const retrieveIrfItems = async (spId) => {
return new Promise((resolve, reject) => {
let spQuery = "SITE" + SpQueryExtend1 + spQueryExpand;
let _items = [];
try{
const axiosApiItem = await axiosApi.get(SiteUrl + spQuery);
const item = axiosApiItem.data.d;
_item.push({
Author: await setPeople(item.Author.Title),
Title: item.Title,
...
Requester: await setPeople(item.Requester.Title)
});
return resolve(_items);
}catch(e){
return reject(e);
}
});
}
Async / await is a way to consume async Promise functions. The async keyword tells the javascript compiler that this function will consume one or more asynchronous functions. The await keyword tells the compiler that the next function call is asynchronous and returns a promise.
In your code, your first then() function should return a promise which is not the case, that's why the second then() can't be reached.
Also, in your code, your new Promise doesn't return anything. When you create a new Promise, you have to end it by calling resolve() or reject.
Hope it helps...
I'm quite a newbie in JavaScript and in Promises.
I'm trying to build an array of objects that I get from an API.
To do so, I've build two functions in a file MyFile.js.
The first one returns a promise when an axios promise is resolved. It's
function get_items (url) {
return new Promise((resolve, reject) => {
let options = {
baseURL: url,
method: 'get'
}
axios(options)
.then(response => {
resolve(response.data)
})
.catch(error => {
reject(error.stack)
})
})
}
The second one looks like this:
let output = []
let next_url = 'https://some_url.com/api/data'
async function get_data () {
try {
let promise = new Promise((resolve, reject) => {
if (next_url) {
get_items(next_url)
.then(response => {
output.push(...response.results)
if (response.next) {
next_url = response.next
console.log('NEXT_URL HERE', next_url)
get_data()
} else {
console.log('else')
next_url = false
get_data()
}
})
.catch(error => {
reject(error.stack)
})
} else {
console.log('before resolve')
resolve(output)
}
})
return await promise
} catch(e) {
console.log(e)
}
}
It's where I'm grinding my teeth.
What I think I understand of this function, is that:
it's returning the value of a promise (that's what I understand return await promise is doing)
it's a recursive function. So, if there is a next_url, the function continues on. But if there is not, it gets called one last time to go into the else part where it resolves the array output which contains the results (values not state) of all the promises. At least, when I execute it, and check for my sanity checks with the console.log I wrote, it works.
So, output is filled with data and that's great.
But, when I call this function from another file MyOtherFile.js, like this:
final_output = []
MyFile.get_data()
.then(result => {
console.log('getting data')
final_output.push(...result)
})
it never gets into the then part. And when I console.log MyFile.get_data(), it's a pending promise.
So, what I would like to do, is be able to make get_data() wait for all the promises result (without using Promise.all(), to have calls in serie, not in parallel, that would be great for performances, I guess?) and then be able to retrieve that response in the then part when calling this function from anywhere else.
Keep in mind that I'm really a newbie in promises and JavaScript in general (I'm more of a Python guy).
Let me know if my question isn't clear enough.
I've been scratching my head for two days now and it feels like I'm running in circle.
Thanks for being an awesome community!
This is a bit untested
const api_url = 'https://some_url.com/api/data';
get_data(api_url).then((results) => {
console.log(results);
}).catch((error) => {
// console.error(error);
});
function get_items (url) {
const options = {
baseURL: url,
method: 'get'
};
return axios(options).then((response) => response.data);
}
async function get_data(next_url) {
const output = [];
while (next_url) {
const { results, next } = await get_items(next_url);
output.push(...results);
next_url = next;
}
return output;
}
Basically it makes things a bit neater. I suggest to look at more examples with Promises and the advantage and when to ease await/async. One thing to keep in mind, if you return a Promise, it will follow the entire then chain, and it will always return a Promise with a value of the last then.. if that makes sense :)
There are a few problems. One is that you never resolve the initial Promise unless the else block is entered. Another is that you should return the recursive get_data call every time, so that it can be properly chained with the initial Promise. You may also consider avoiding the explicit promise construction antipattern - get_items already returns a Promise, so there's no need to construct another one (same for the inside of get_items, axios calls return Promises too).
You might consider a plain while loop, reassigning the next_url string until it's falsey:
function get_items (baseURL) {
const options = {
baseURL: url,
method: 'get'
}
// return the axios call, handle errors in the consumer instead:
return axios(options)
.then(res => res.data)
}
async function get_data() {
const output = []
let next_url = 'https://some_url.com/api/data'
try {
while (next_url) {
const response = await get_items(next_url);
output.push(...response.results)
next_url = response.next;
}
} catch (e) {
// handle errors *here*, perhaps
console.log(e)
}
return output;
}
Note that .catch will result in a Promise being converted from a rejected Promise to a resolved one - you don't want to .catch everywhere, because that will make it difficult for the caller to detect errors.
Another way of doing it is to not use async at all and just recursively return a promise:
const getItems = (url) =>
axios({
baseURL: url,
method: 'get',
}).then((response) => response.data);
const getData = (initialUrl) => {
const recur = (result, nextUrl) =>
!nextUrl
? Promise.resolve(result)
: getItems(nextUrl).then((data) =>
recur(result.concat([data.results]), data.next),
);
return recur([],initialUrl)
.catch(e=>Promise.reject(e.stack));//reject with error stack
};
As CertainPerformance noted; you don't need to catch at every level, if you want getData to reject with error.stack you only need to catch it once.
However; if you had 100 next urls and 99 of them were fine but only the last one failed would you like to reject in a way that keeps the results so far so you can try again?
If you do then the code could look something like this:
const getData = (initialUrl) => {
const recur = (result, nextUrl) =>
!nextUrl
? Promise.resolve(result)
: getItems(nextUrl)
.catch(e=>Promise.reject([e,result]))//reject with error and result so far
.then((data) =>
recur(result.concat([data.results]), data.next),
);
return recur([],initialUrl);//do not catch here, just let it reject with error and result
};
I'm really confused about why I can not return the JSON result from amazonMws.products.search() and could use some help understanding what is going on. When I write it this way gives me undefined:
function listMatchingProducts(query) {
const options = {
Version: VERSION,
Action: 'ListMatchingProducts',
MarketplaceId: MARKET_PLACE_ID,
SellerId: SELLER_ID,
Query: query
}
amazonMws.products.search(options, (err, res) => {
if(err){
throw(err)
return
}
return res
})
}
I also get undefined when using amazonMws.products.search().then().catch() as well.
If I return amazonMws.products.search() I get a promise back instead of the result.
Inside of the callbacks if I console.log(res) I get back the JSON result I'm expecting. So this led me to believe I need to use async await I think, but this results in Promise { <pending> }:
async function listMatchingProducts(query) {
const options = {
Version: VERSION,
Action: 'ListMatchingProducts',
MarketplaceId: MARKET_PLACE_ID,
SellerId: SELLER_ID,
Query: query
}
return await amazonMws.products.search(options)
.then(res => {
return res
})
.catch(e => errorHandler(e))
}
I am totally lost, so if someone could explain to me what is going on, that would be greatly appreciated.
The amazonMws.products.search function is asynchronous, meaning that it will give you a value later, and because of this, you can't get the value now. Instead, you'll have to say what you want to do later when you receive the value.
This is what returning the promise does. The promise itself is the representation of this value that you'll receive later.
function listMatchingProducts(query) {
const options = {
Version: VERSION,
Action: 'ListMatchingProducts',
MarketplaceId: MARKET_PLACE_ID,
SellerId: SELLER_ID,
Query: query
}
return amazonMws.products.search(options)
}
Then, when calling the function, attach a handler to the promise.
listMatchingProducts(someQuery)
.then(result => {/* do something with result */})
.catch(error => {/* handle the error */})
And, though you don't need to use async await here, it can make the code look a little nicer in some situations, as though it were synchronous. Here's what calling the above function would look like with async await:
async function getProducts() {
try {
const result = await listMatchingProducts(someQuery)
// do something with result
} catch (error) {
// handle the error
}
}
And, as usual, always consult the docs for any detail you're confused about:
Using promises
await keyword
function listMatchingProducts(query) {
const options = {
Version: VERSION,
Action: 'ListMatchingProducts',
MarketplaceId: MARKET_PLACE_ID,
SellerId: SELLER_ID,
Query: query
}
return amazonMws.products.search(options); //returns a promise
}
As others have pointed out, Promises just don’t work the way you think they do.
See my answer to function returning too early before filter and reduce finish for a (hopefully) clear explanation of the problem you face.
Let's say I have two async events, both need to i/o with remote exchange.
placeOrder()
cancelOrder()
Both events fire in async way, which means cancelOrder can be called before placeOrder return. Tricky part is I need the placeOrder to return an Order ID first otherwise there is no way to call cancelOrder, so I need some way to block the cancelOrder event right until placeOrder returns, and the blockage cannot be too long otherwise the Order may be executed, so loop/timeout/frequent checking doesn't work here.
Any idea?
You would use a Promise for that. If your functions already return a promise, you can simply chain the both functions using then()
placeOrder().then(val => cancelOrder(val));
If they do not, you can put them inside a new Promise
function foo() {
return new Promise((resolve, reject) => {
// do stuff
resolve('<result of placeOrder here>');
});
}
function bar(val) {
return new Promise((resolve, reject) => {
// do stuff
resolve('whatever')
})
}
and call
foo()
.then(value => bar(value))
.then(console.log);
If you are able to use ES2017, the you can use async functions. For example, I'm going to assume that your functions perform some sort of request to the database using fetch or axios since you haven't specified. Then you can write placeOrder and cancelOrder like so:
const placeOrder = async () => {
try {
const response = await fetch('/place_order');
// Do something with the response
} catch (err) {
// Handle error
}
};
const cancelOrder = async () => {
try {
const response = await fetch('/cancel_order');
// Do something with the response
} catch (err) {
// Handle error
}
};
const someFunction = async () => {
await placeOrder();
await cancelOrder();
};