This question already has answers here:
Waiting for the fetch to complete and then execute the next instruction
(2 answers)
How can I return the fetch API results from a function?
(5 answers)
Closed last month.
My goal is to "pause" the EventListener function in order to get something done by calling another function (getCategoriesIDs) that returns a value that I will use to continue the EventListener function execution.
When I log the categoriesIDtoNameMapped after the execution it comes out as UNDEFINED.
Would greatly appreciate your help.
form.addEventListener("submit", async (e) => {
//do something
try {
let categoriesIDtoNameMapped = await getCategoriesIDs()
console.log(categoriesIDtoNameMapped)
} catch (err) {
console.log(err)
}
//do something with the categoriesIDtoNameMapped
}
function getCategoriesIDs() {
fetch('http://localhost:1337/api/categories', {
method: 'GET',
headers: {
'Content-type': 'application/json',
Authorization: `Bearer ${JWT_TOKEN}`
}
})
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then((response) => {
const categoriesIDtoNameMapped = {}
for (let i = 0; i < response.data.length; i++) {
categoriesIDtoNameMapped[response.data[i].id] = response.data[i].attributes.name
}
return new Promise(resolve => {
resolve(categoriesIDtoNameMapped)
});
});
}
Your getCategoriesIDs needs to be an async function for you to await something on it.
To fix this you need make getCategoriesIDs as async function and use await for fetch
async function getCategoriesIDs() {
const response = await fetch('http://localhost:1337/api/categories', {
method: 'GET',
headers: {
'Content-type': 'application/json',
Authorization: `Bearer ${JWT_TOKEN}`
}
})
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const responseJson = await response.json();
const categoriesIDtoNameMapped = {}
for (let i = 0; i < response.data.length; i++) {
categoriesIDtoNameMapped[responseJson.data[i].id] = response.data[i].attributes.name
}
return categoriesIDtoNameMapped;
}
Related
This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 10 months ago.
let metadata = [];
allNFTs.map(async (e) => {
if (e.metadata) {
metadata.push(JSON.parse(e.metadata).attributes);
} else {
let config = {
method: "get",
url: `http://localhost:3000/api/fetch`,
header: {
"Content-Type": "application/json",
},
};
const res = await axios(config);
const attr = res.data.attributes;
metadata.push(attr);
console.log(metadata); // this one worked after below
}
});
console.log(metadata); // this one worked before above
But i want to wait till my axios done fetching, so i can finally console.log that my actual metadata.
Make an array of promises, and then await them with Promise.all
const metadataPromises = allNFTs.map((e) => {
if (e.metadata) {
return Promise.resolve(JSON.parse(e.metadata).attributes);
} else {
let config = {
method: "get",
url: `http://localhost:3000/api/fetch`,
header: {
"Content-Type": "application/json",
},
};
return axios(config).then((res) => res.data.attributes);
}
});
// await still has to be in an async function
const metadata = await Promise.all(metadataPromises);
console.log(metadata);
// or use .then
Promise.all(metadataPromises).then((metadata) => console.log(metadata));
The issue with you code, that you don't wait. The latest console.log is execurted before the map iterating all the items.
You should use somehting like that: https://www.npmjs.com/package/modern-async
E.g.
async function init() {
var ma= require('modern-async')
await ma.mapSeries(allNFTs,async (e)=>{
if (e.metadata) {
metadata.push(JSON.parse(e.metadata).attributes);
} else {
let config = {
method: "get",
url: `http://localhost:3000/api/fetch`,
header: {
"Content-Type": "application/json",
},
};
const res = await axios(config);
const attr = res.data.attributes;
metadata.push(attr);
console.log(metadata);
}
})
console.log(metadata);
}
The code below receives all of the user's redeemed rewards from an API, I need to stop the loop from completing them all at once.
The for loop runs through all of the rewards the current user has redeemed through Twitch's API, then fulfills them if specific conditions are met. I want it to only fulfill one redemption, not all (x) amount of them.
The fulfill reward part happens at: fulfillReward()
For a full code snippet, click here: https://pastebin.com/7k5WNhmD
// looping over reward returned data
for (let i = 0; i < rewards.length; i++) {
async function fulfillReward() {
await fetch(
`https://api.twitch.tv/helix/channel_points/custom_rewards/redemptions?broadcaster_id=58606718&reward_id=08d5e2d9-ddd7-4082-bc78-39b06b35cd68&id=${rewards[i].id}`,
{
method: 'PATCH',
headers: {
'client-Id': process.env.TWITCHBOT_CLIENT_ID,
Authorization: `Bearer ${process.env.TWITCHBOT_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
status: 'FULFILLED',
}),
}
)
}
const currentReward = rewards[i]
const currentRewardUsername = currentReward.user_name.toLowerCase()
if (currentRewardUsername === input.toLowerCase()) {
countDocuments(discordID)
.then(() => {
return findOneUser(discordID)
})
.then(() => {
// All (x) amount of rewards get fulfilled instead of the first matching result
fulfillReward()
interaction.reply('success')
})
.catch((err) => console.log(err))
} else if (currentRewardUsername != input.toLowerCase()) {
return interaction.reply(`The Twitch user **${input}** has not redeemed the channel reward!`)
}
}
The general solution to exit a loop when a condition is met, is to incorporate a break statement in whatever conditional decides the loop has achieved its purpose early.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break
I've made a simple snippet that runs a million cycle for-next loop but terminates after 10 loops when a condition is met, using break.
let loopNumber = 0;
for (let i=0; i<1000000; i++)
{
loopNumber++;
if (loopNumber==10) {
break;
}
} // next i;
console.log(loopNumber);
Not sure I understood completely what you actually want but lets try:
async function fulfillReward(reward) {
await fetch(
`https://api.twitch.tv/helix/channel_points/custom_rewards/redemptions?broadcaster_id=58606718&reward_id=08d5e2d9-ddd7-4082-bc78-39b06b35cd68&id=${reward.id}`,
{
method: 'PATCH',
headers: {
'client-Id': process.env.TWITCHBOT_CLIENT_ID,
Authorization: `Bearer ${process.env.TWITCHBOT_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
status: 'FULFILLED',
}),
}
)
}
If you just receive the reward as a parameter you don't need to declare functions inside the for (please don't ever do that)
Then you call it sending the reward parameter
if (currentRewardUsername === input.toLowerCase()) {
countDocuments(discordID)
.then(() => {
return findOneUser(discordID)
})
.then(() => {
fulfillReward(currentReward)
interaction.reply('success')
})
.catch((err) => console.log(err))
} else if (currentRewardUsername != input.toLowerCase()) {
return interaction.reply(`The Twitch user **${input}** has not redeemed the channel reward!`)
}
I updated your code here https://pastebin.com/clone/7k5WNhmD, but cant really test it, so please let me know if you need something else
try this.
for (let i = 0; i < rewards.length; i++) {
async function fulfillReward() {
await fetch(
`https://api.twitch.tv/helix/channel_points/custom_rewards/redemptions?broadcaster_id=58606718&reward_id=08d5e2d9-ddd7-4082-bc78-39b06b35cd68&id=${rewards[i].id}`,
{
method: 'PATCH',
headers: {
'client-Id': process.env.TWITCHBOT_CLIENT_ID,
Authorization: `Bearer ${process.env.TWITCHBOT_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
status: 'FULFILLED',
}),
}
)
}
const currentReward = rewards[i]
const currentRewardUsername = currentReward.user_name.toLowerCase()
if (currentRewardUsername === input.toLowerCase()) {
function doSomething() {
return new Promise((resolve, reject) => {
countDocuments(discordID)
.then(() => {
return findOneUser(discordID)
})
.then(() => {
// All (x) amount of rewards get fulfilled instead of the first matching result
fulfillReward()
interaction.reply('success')
resolve(true);
})
.catch((err) => console.log(err))
})
}
const ret = await doSomething();
if (ret)
return ;
} else if (currentRewardUsername != input.toLowerCase()) {
return interaction.reply(`The Twitch user **${input}** has not redeemed the channel reward!`)
}
}
I'm struggling to return the synchronous results of the method below.
I call the method from a different method:
var result = this.getVendor(id)
console.log(result)
Here is the fetch method:
methods: {
async getData(id) {
const response = await fetch(`${API_URL}api/${id}`, {
method: "GET",
headers: {
authorization: `Bearer ${localStorage.token}`
}
})
.then(res => res.json())
.then(data => {
return data;
});
await response;
}
}
How do I return the results response of the getData() function to show in the console?
Async functions Always return a promise.
You can use the await syntax to return it properly.
async getData(id) {
const response = await fetch(`${API_URL}api/${id}`, {
method: "GET",
headers: {
authorization: `Bearer ${localStorage.token}`
}
})
const data = await response.json()
return data
}
You can access the data out of that function anywhere you call it.
let data = null
object.getData(2)
.then(x => {
data = x
})
Also if you are going to use async await make sure to use try and catch to handle any errors that come up.
async getData(id) {
try {
const response = await fetch(`${API_URL}api/${id}`, {
method: "GET",
headers: {
authorization: `Bearer ${localStorage.token}`
}
})
const data = await response.json()
return data
}
} catch(err) {
console.error(err)
}
const displayCharacters = async () => {
if(filteredCharacters !== 'default'){
const a = filteredCharacters.map(e => e.name);
const options = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ 'data' : a })
};
const b = await fetch("/image",options).then(res => res.json())
return b;
}else{
return "yikes";
}
}
console.log(displayCharacters());
I have this fetch request but when I log the results this is what i see :
Promise {<resolved>: "yikes"}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: "yikes"
I just want the promiseValue and not this whole thing around it. How do i do this?
the async function returns a promise instantly, without waiting for the promise to resolve. You may instead console.log inside the function:
const displayCharacters = async () => {
if(filteredCharacters !== 'default'){
const a = filteredCharacters.map(e => e.name);
const options = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ 'data' : a })
};
try {
const b = await fetch("/image",options).then(res => res.json());
console.log(b);
//the better practice is however, to do like:
const b = await fetch("/image",options)
const result = await b.json();
console.log(result );
}
catch(err) {
console.log(err);
}
}else{
console.log("yikes");
}
}
displayCharacters();
The best way that i know to use fetch goes something like this:
const displayCharacters = async () => {
if(filteredCharacters !== 'default'){
const a = filteredCharacters.map(e => e.name);
const options = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ 'data' : a })
};
const b = await fetch("/image",options)
.then(res => {
// Handle API Errors
if (!res.ok) {
throw Error(res.statusText);
}
// Return if no errors
return res.json();
})
// this is the data you want
.then(data => data)
// it will only reject on network failure or if anything prevented the request from completing
.catch(error => {
console.log(error.message)
});
return b;
}else{
return "yikes";
}
}
Basically you chain two thens and a catch to completely understand the response
- the first then checks for api level errors
- second then gets you the data
- catch is invoked in case when it is not able to reach the api itself like connection issues
This question already has answers here:
What's the difference between returning value or Promise.resolve from then()
(6 answers)
Closed 4 years ago.
I have questions about promises. I'm just starting to deal with them and it's not that easy to understand!
I'm trying to setup an authentication system for my app.
RegisterPage
handleSubmit looks like that:
handleSubmit(event) {
event.preventDefault();
const { user } = this.state;
//some code here
userActions.register(user);
}
UserActions
function register(user) {
userService.register(user)
.then(
user => {
success(user);
},
error => {
failure(error.toString());
}
);
function success(user) { return { type: "REGISTER_SUCCESS", user } }
function failure(error) { return { type: "REGISTER_ERROR", error } }
}
UserService
function register(user) {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user)
};
return fetch(`/api/users/register`, requestOptions).then(handleResponse);
}
function handleResponse(response) {
return response.text().then(text => {
const data = text && JSON.parse(text);
if (!response.ok) {
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
return data;
});
}
Question 1. That code is "working" but not like I want. That way, even if the request success, I still can have error from the server, like duplicate username or something like that. I guess what I want is to return Promise.reject() not just if !response.ok but also if I have errors in the JSON returned right?
function handleResponse(response) {
return response.text().then(text => {
const data = text && JSON.parse(text);
if (!response.ok) {
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
else if(data.errors) {
return Promise.reject(data.message);
}
return data;
});
}
Question 2. If everything's fine, should I return data or return Promise.resolve(data)? And why?
Checkout the documentation for fetch here: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful
It seems you should be using .catch() to get server errors and just use throw new Error() for having errors.
You don't really need to use Promise.resolve or Promise.reject.
To help refactor what you have, you can try this
function register(user) {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user)
};
return fetch(`/api/users/register`, requestOptions).then(handleResponse);
}
function handleResponse(response) {
return response.text()
.then(text => {
if (!response.ok) {
const error = (data && data.message) || response.statusText;
throw new Error(error);
} else {
const data = text && JSON.parse(text);
return data;
}
})
.catch(error => throw new Error(err));
}