I am doing an api call that returns a promise. The call works fine but I want to do treat data contained within the promise. Here is My call:
let promiseArray = this.get('store').query('member', {query: term, option: this.get('option')});
promiseArray.then(members => {console.log(members);
});
let var= members;
console.log(var);
My problem is that this doesn't return an array of my model i.e members, also the second display of members display undefined , it return an object containing a lot of meta data, also the array but inside some meta data.
How could I get simply the array ?
You can use async await for your purpose.
const promiseFunc = () => {
// Return the promise and await this inside a async function
return this.get('store').query('member', {query: term, option: this.get('option')});
}
const asyncFunc = async () => {
const value = await promiseFunc();
console.log(value);
}
asyncFunc();
Related
I am basically trying to send multiple API queries at once and wait for the result. The code works well for the first query, but I am actually struggling to reset the value of the prior async return. After the first execution is completed, it keeps returning the very first values. The goal is to assign random href(URL) with every function's execution and recall the API once again, so it can call the API once again with a newly randomized href and return new values. Here is the code, would be great is someone could help:
async function fetchData() {
const [test0_array_response,
test1_array_response,
test2_array_response,
] = await Promise.all([
fetch(random_href+'/test0/'),
fetch(random_href+'/test1/'),
fetch(random_href+'/test2/'),
]);
const test0_array = await test0_array_response.json();
const test1_array= await test1_array_response.json();
const test2_array = await test2_array_response.json();
return [test0_array, test1_array, test2_array];
}
function callApi() {
console.log(fetchData()) // returns a promise
};
From what I can see random_href is part of the closure of fetchData, so you need to pass it as argument inside callApi or wherever you use fetchData in order to use his correct value.
Take a look here for learn what I means with closure.
You should do something like this:
async function fetchData(random_href) {
const [test0_array_response,
test1_array_response,
test2_array_response,
] = await Promise.all([
fetch(random_href+'/test0/'),
fetch(random_href+'/test1/'),
fetch(random_href+'/test2/'),
]);
const test0_array = await test0_array_response.json();
const test1_array= await test1_array_response.json();
const test2_array = await test2_array_response.json();
return [test0_array, test1_array, test2_array];
}
function callApi(random_href) {
console.log(fetchData(random_href)) // returns a promise
};
Another approach could be to make an object random_container = { href: 'VALUE'} and use it's reference inside the fetchData function. So, for example, fetch(random_container.href+'/test0/'). In this way random_container will be part of the closure of fetchData but you'll be able to read the last value of random_container.href.
I have an array of objects containing metadata for videos. I need to iterate through it and for each object I need to make an API call passing its filename to get a streamingLink and assign it to the object's streamingLink. My problem is by the time I am returning this array, the array is undefined. How do I tell the code to wait until streamingLink has been assigned?
Here's kind of what my code looks like:
// get array from database
const items = await client.db('database').collection('collection').find({}).toArray();
// for each object get and replace streamingLink
let items_withLinks = items.map((item) => {
getStreamingLink(item.filename) // API call to get link
.then(response => {
item.streamingLink = response.result.link;
return item;
})
});
console.log(items_withLinks); // undefined
I have tried using await in different places and wrapping this in an async function but I can't figure out the right way to do it.
You can loop over your array and create a promise for each item. Then you can call Promise.all.
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// expected output: Array [3, 42, "foo"]
The fundamental problem is that the function you are using in the .map doesn't return anything. (So you will actually technically get an array of undefined values, rather than undefined itself - but that obviously is no good.)
You need to return the promise and then use Promise.all to get the array of results.
// get array from database
const items = await client.db('database').collection('collection').find({}).toArray();
// for each object get and replace streamingLink
let items_withLinks = items.map((item) => {
// return is the only thing I've added here!
return getStreamingLink(item.filename) // API call to get link
.then(response => {
item.streamingLink = response.result.link;
return item;
})
});
const result = await Promise.all(items_withLinks);
console.log(result);
I'm hoping somebody can point out the error of my ways here.
I have two functions at the moment. One is getData and it's an async function that simply makes an API call requesting data. The second function is getRandomCategories that encapsulates the getData function call and holds the value of that async operation in a variable called res. The rest of the code within getRandomCategories manipulates the response data to an array of numbers where each number represents a category.
When I use the debugger statement in the getRandomCategories function (right before the return statement within the try block) I'm getting the data type I'm expecting from my variable named apiCallCategoryArray - it's an array of numbers each representing a category. Life is good.
Here's what I'm not understanding: If you look at the two function calls towards the bottom of the snippet (test() and testAgain() they're essentially the same functions but just with different names so I can compare them side-by-side) I'm able to console.log(res) in test() with the expected return value (an array of numbers) but when I try to return res (in testAgain()) the variable myData2 does NOT contain an array of numbers (as what I'm expecting it to contain) instead it contains: Promise { <state>: "pending" }
Here's my code below:
async function getData(endpoint, query, value) {
return await axios.get(
`http://jservice.io/api/${endpoint}?&${query}=${value}`
)
}
// createa a function that will return 6 random categories
async function getRandomCategories(data) {
try {
const res = await getData('categories', 'count', 50)
const data = res.data;
const categories = filterCategoryData(data); // I'm filtering for categories
const categoryIdArr = mapCategoryIds(categories); // an array of just category Ids
const shuffledCategoryIds = shuffle(categoryIdArr); // shuffles the array
const apiCallCategoryArray = takeFirstXItems(shuffledCategoryIds, 6); // takes 6 items
debugger // values are as expected
return apiCallCategoryArray
} catch (err) {
console.log(err);
}
}
// this logs what my expected return value should be
async function test() {
const res = await getRandomCategories()
console.log(res) // Array(6) [ 11518, 11537, 11533, 11512, 11542, 11506 ]
}
// But when I try to use the return statement so I can use the array
// I'm getting a promise back. This NOT my expected return value??
async function testAgain() {
const res = await getRandomCategories()
return res // return statement instead of console.log()
}
const myData = test() // the console.log within the test function logs the expected data type
console.log(myData) // but if I console.log myData it logs: Promise { <state>: "pending" }
const myData2 = testAgain() // same function but just a different name but this time I'm using a return statement instead of console.log
console.log(myData2) // Promise { <state>: "pending" }
My question is: why am I able to console.log(res) with the expected return value in test()
but when I use the return statement (like in testAgain()) I'm getting a Promise { : "pending" } instead of the array of numbers like I'm expecting. Where do I tweak my code so that my variable holds an array of numbers instead of the Promise?
Thanks in advance for everyone's time.
const data = res.data; should be const data = await res.data; in the getRandomCategories function. Also const myData2 = testAgain() should be const myData2 = await testAgain() but as it is top level code and await needs an async function, you could wrap it into an self-invoking function or use then/catch.
More info about async/await here.
I'm fetching my user data and the map function is called several times for each user. I want to wait until all data was pushed to the array and then manipulate the data. I tried using Promise.all() but it didn't work.
How can I wait for this map function to finish before continuing?
Needless to say that the array user_list_temp is empty when printed inside the Promise.all().
const phone_list_promise_1 = await arrWithKeys.map(async (users,i) => {
return firebase.database().ref(`/users/${users}`)
.on('value', snapshot => {
user_list_temp.push(snapshot.val());
console.log(snapshot.val());
})
}
);
Promise.all(phone_list_promise_1).then( () => console.log(user_list_temp) )
I changed the code to this but I still get a wrong output
Promise.all(arrWithKeys.map(async (users,i) => {
const eventRef = db.ref(`users/${users}`);
await eventRef.on('value', snapshot => {
const value = snapshot.val();
console.log(value);
phone_user_list[0][users].name = value.name;
phone_user_list[0][users].photo = value.photo;
})
console.log(phone_user_list[0]);
user_list_temp.push(phone_user_list[0]);
}
));
console.log(user_list_temp); //empty array
}
It is possible to use async/await with firebase
This is how I usually make a Promise.all
const db = firebase.database();
let user_list_temp = await Promise.all(arrWithKeys.map(async (users,i) => {
const eventRef = db.ref(`users/${users}`);
const snapshot = await eventref.once('value');
const value = snapshot.value();
return value;
})
);
This article gives a fairly good explanation of using Promise.all with async/await https://www.taniarascia.com/promise-all-with-async-await/
Here is how I would refactor your new code snippet so that you are not mixing promises and async/await
let user_list_temp = await Promise.all(arrWithKeys.map(async (users,i) => {
const eventRef = db.ref(`users/${users}`);
const snapshot= await eventRef.once('value');
const value = snapshot.val();
console.log(value);
phone_user_list[0][users].name = value.name; // should this be hardcoded as 0?
phone_user_list[0][users].photo = value.photo; // should this be hardcoded as 0?
console.log(phone_user_list[0]);
return phone_user_list[0]; // should this be hardcoded as 0?
})
);
console.log(user_list_temp);
Here is a simple example that uses fetch, instead of firebase:
async componentDidMount () {
let urls = [
'https://jsonplaceholder.typicode.com/todos/1',
'https://jsonplaceholder.typicode.com/todos/2',
'https://jsonplaceholder.typicode.com/todos/3',
'https://jsonplaceholder.typicode.com/todos/4'
];
let results = await Promise.all(urls.map(async url => {
const response = await fetch(url);
const json = await response.json();
return json;
}));
alert(JSON.stringify(results))
}
If I understand your question correctly, you might consider revising your code to use a regular for..of loop, with a nested promise per user that resolves when the snapshot/value for that user is available as shown:
const user_list_temp = [];
/*
Use regular for..of loop to iterate values
*/
for(const user of arrWithKeys) {
/*
Use await on a new promise object for this user that
resolves with snapshot value when value recieved for
user
*/
const user_list_item = await (new Promise((resolve) => {
firebase.database()
.ref(`/users/${users}`)
.on('value', snapshot => {
/*
When value recieved, resolve the promise for
this user with val()
*/
resolve(snapshot.val());
});
}));
/*
Add value for this user to the resulting user_list_item
*/
user_list_temp.push(user_list_item);
}
console.log(user_list_temp);
This code assumes that the enclosing function is defined as an asynchronous method with the async keyword, seeing that the await keyword is used in the for..of loop. Hope that helps!
I realise very similar questions have been answered before, but I'm still finding it very confusing as to how this works...
From my understanding promises are used to deal with asyc requests - these promises essentially send back the state or a "promise" that at some point later a JSON body (or other object) will be delivered.
What I'm trying to understand is how I properly handle these requests so that the function doesn't return until the JSON body is ready to be parsed.
Below I'm trying to simply extract the key "result" (which returns a string "result") and parse it to another variable that can be stored and then later used somewhere else in my code. Unfortunately, my code always returns a [Object Promise], rather than the extracted JSON. I believe this is because response.json is also a promise... however, I don't understand how I get out of the "chain of promises" and return a value that I can actually do something with.
Thanks for any advice,
async function name() {
const response = await fetch('https://xxxxx.herokuapp.com/timespent', {});
const json = await response.json();
return json.result;
}
let varr = name();
console.log(varr)
Since your function is async it always return a promise. You need to use await for result.
read more about async here
async function name() {
const response = await fetch('https://mautargets.herokuapp.com/timespent', {});
const json = await response.json();
return json.result;
}
async function result(){
//await can only be called from inside of async function. So we need async function for await name()
let varr = await name();
console.log(varr) // Success
}
result()
In your example code, name function is declared async, so it returns a promise.
Inside that function body, you correctly handle async calls like fetch or the JSON transformation.
What you need now is either use await to wait for the function to "resolve", or use the "older" then/catch promises methods. Note that you cannot always use await outside an async function so you may need to wrap it.
Example :
async function name() {
const response = await fetch('https://mautargets.herokuapp.com/timespent', {});
const json = await response.json();
return json.result;
}
// using promise.then
name().then(result => console.log(result));
// wrapping await
(async function test() {
try{
console.log(await name());
}catch(error) {
// error goes here if promise got rejected
}
})()
You could have a callback in the function declaration, and use '.then(...)' and call it when the promise has been resolved:
async function name(cb) {
const response = await
fetch('https://mautargets.herokuapp.com/timespent', {});
const json = response.json();
json.then(x => cb(x))
}
name(console.log)
This is because you're using an Async function, which will return a promise.
Or if you would like the method to return, you could either call it in another Asynchronous context and utilize await again:
// Assume no callback: code just as you had it.
async function wrapper() {
console.log(await name())
}
Or you could do name().then(...) as specified before:
// Assume no callback: code just as you had it.
name().then(console.log)
Hope this helps!
I'm actually looking for the answer(same as yours), so I found this way.
I. ASK/REQUEST for data
async function fetchMyJson() {
const response = await fetch('https://1stAPI.devdeveloper1.repl.co/fiveD');
const myData = await response.json();
return myData;
}
II.GET Extract data
fetchMyJson().then(myData => {
let myData_output = myData.USD[0].rate; // fetched or Get OUTPUT data
console.log(myData_output);
document.body.innerHTML = `<div>${myData_output}</div>`; //make sure you add ${} for output
});
async function fetchMyJson() {
const response = await fetch('https://1stAPI.devdeveloper1.repl.co/fiveD');
const myData = await response.json();
return myData;
}
//GET Extract data
fetchMyJson().then(myData => {
let myData_output = myData.USD[0].rate; // fetched or Get OUTPUT data
console.log(myData_output);
document.body.innerHTML = `<div>${myData_output}</div>`; //make sure you add ${} for output
});
It is correct that you await fetch and .json since they are async.
async function name() {
const response = await fetch('http://blah.com/api', {});
const json = await response.json();
return json.result;
}
However, async and Promises inside function name make it async too. So the return value of name is a Promise that you should await it, or .then it, like:
// Old style .then
name().then(result => console.log(result))
// Modern style await
async function main() {
const result = await name()
console.log(result)
}