The question is, how can I get rid of calling second fetch 300 times? Or is there another way to do that, what I`m doing?
Additionally how to do ordered(don`t wanna sort) calls of first api, because they`re coming from api in chaotic asynchronous way?
for(let i=1;i<=300; i++) {
fetch(`example.api/incomes/${i}`) // should be returned 300 times
.then(response => {
if(response.ok) return response.json();
throw new Error(response.statusText);
})
.then(function handleData(data) {
return fetch('example.api') // should be returned 1 time
.then(response => {
if(response.ok) return response.json();
throw new Error(response.statusText);
})
})
.catch(function handleError(error) {
console.log("Error" +error);
});
};
You can solve it using Promise all.
let promises = [];
for (let i = 1; i <= 300; i++) {
promises.push(fetch(`example.api/incomes/${i}`));
}
Promise.all(promises)
.then(function handleData(data) {
return fetch("example.api") // should be returned 1 time
.then(response => {
if (response.ok) return response.json();
throw new Error(response.statusText);
});
})
.catch(function handleError(error) {
console.log("Error" + error);
});
Store all of your requests in an array. Then use Promise.all() the wait for all of those requests to finish. Then when all of the requests are finished, use another Promise.all() with a map() inside of it to return the the JSON of each request and wait for all of those to finish.
Now your data argument will have an array of objects available in the next then callback.
function fetch300Times() {
let responses = [];
for(let i = 1; i <= 300; i++) {.
let response = fetch(`example.api/incomes/${i}`);
responses.push(response);
}
return Promise.all(responses);
}
const awaitJson = (response) => Promise.all(responses.map(response => {
if(response.ok) return response.json();
throw new Error(response.statusText);
}));
fetch300Times()
.then(awaitJson)
.then(data => {
fetch('example.api') // should be returned 1 time
.then(response => {
if(response.ok) return response.json();
throw new Error(response.statusText);
});
}).catch(function handleError(error) {
console.log("Error" +error);
});
Related
I am new in JavaScript and I have a problem:
I do fetch('path'), then I assign values and return it. Later I call this function in my other functions but it runs first with empty values without waiting values to be assigned. How can I solve this? I think I should use async and await but do not know exactly how.
function loadLocalJson(path) {
let users = [];
fetch(path)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok :(');
}
return response.json();
})
.then(results => {
users = results
console.log(users);
})
.catch(err => console.log('Error', err));
return users;
}
function getFilteredAndSortedArray(users, price) {
console.log(users, price);
return users.filter(user => {
return user.salary && user.salary > price;
})
.sort(user => user.name);
}
users = loadLocalJson('users.json');
usersB= getFilteredAndSortedArray(users, 1000);
console.log(usersB, usersA);
// PrefixedUsersArray(users)
// ...
You need to understand how asynchronous code works. A fetch request returns a promise, so you must return the promise and access the value using .then():
function loadLocalJson(path) {
return fetch(path)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok :(');
}
return response.json();
})
.catch(err => console.log('Error', err))
}
function getFilteredAndSortedArray(users, price) {
console.log(users, price);
return users.filter(user => {
return user.salary && user.salary > price;
}).sort(user => user.name);
}
loadLocalJson('users.json').then(users => {
usersB = getFilteredAndSortedArray(users, 1000);
console.log(usersB, users);
})
You do not have to assign users, you can just return the promise, sinc eyou have already called response.json(). Also, your sort function most likely won't work, try something like this:
users.sort((a, b) => a.name.localeCompare(b.name))
I have quite an issue for some time and is getting on my nerves and it doesn't make sense. I have used axios on my react frontend and it works perfect when assigning the get value to the state. But when using it in a normal javascript code, I appear to have this following issue: i can print the object's value in the console but it will return only undefined.. Here is my code:
login = () => {
let data;
axios.get('https://myaddress/authenticate')
.then(response => {
data = response;
console.log('data here', data);
})
.catch(error => {
console.error('auth.error', error);
});
console.log('eee', data);
return data;
};
Here we are talking about axios strictly.
You can't return an ajax response because it's asynchronous. You should wrap your function into a promise or pass a callback to login
UPDATE: As #Thilo said in the comments, async/await would be another option, but it will let you set the response to data tho ...
1. Wrap into a promise
login = () => new Promise((resolve, reject)=>{
axios.get('https://myaddress/authenticate')
.then(response => {
resolve(response)
})
.catch(error => {
reject(error)
});
});
// Usage example
login()
.then(response =>{
console.log(response)
})
.catch(error => {
console.log(error)
})
2. Pass a callback
login = (callback) => {
axios.get('https://myaddress/authenticate')
.then(response => {
callback(null,response)
})
.catch(error => {
callback(error,null)
});
};
// Usage example
login((err, response)=>{
if( err ){
throw err;
}
console.log(response);
})
3. Async/Await
login = async () => {
// You can use 'await' only in a function marked with 'async'
// You can set the response as value to 'data' by waiting for the promise to get resolved
let data = await axios.get('https://myaddress/authenticate');
// now you can use a "synchronous" data, only in the 'login' function ...
console.log('eee', data);
return data; // don't let this trick you, it's not the data value, it's a promise
};
// Outside usage
console.log( login() ); // this is pending promise
In ES7/ES8 you can do async/await like a boss:
login = () => {
return new Promise((resolve, reject) => {
axios.get('https://myaddress/authenticate')
.then(response => {
resolve(response)
})
.catch(error => {
console.error('auth.error', error);
reject(error)
});
});
};
async function getData() {
try{
const data = await login()
} catch(error){
// handle error
}
return data;
}
getData()
.then((data) => console.log(data));
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 4 years ago.
I am trying to store the actual response from a Promise to a variable
let token;
let tokenPromise = new Promise(function(resolve, reject) {
fetch(tokenUrl, options)
.then(res => {
if (res.ok) {
return res.json()
} else {
reject('Not clean')
}
}).then((data) => {
resolve(data.access_token)
})
})
token = tokenPromise.then(res => {return res})
return token
When this is run, token returns as the Promise object and not the actual response. How do I store the actual response or a value from the response as a variable within the parent function?
Because the function then returns a promise.
An alternative is to use an async function.
function getToken() {
let tokenPromise = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('myNewToken');
}, 100);
});
return tokenPromise;
}
async function main() {
let token = await getToken();
console.log(token);
}
main();
Why you create a new Promise around fetch?
function status(res) {
if (res.ok) {
return Promise.resolve(res)
} else {
return Promise.reject(new Error(res.statusText))
}
}
function json(res) {
return res.json()
}
function getToken() {
return fetch(tokenUrl, options) // Fetch URL
.then(status) // Is everything nice?
.then(json) // Response to json
.then(jsonData => jsonData.access_token) // return access_token
.catch(function(error) { // Some S*ht Happens :(
console.log('Request failed', error);
/* ErrorHandling Stuff */
});
}
You could use ES7 async/await
let token;
let tokenPromise = async () => {
try {
const res = await fetch(tokenUrl, options);
const data = await res.json();
return data.access_token
} catch (error) {
console.error('Error fetching token', error);
return 'Not clean';
}
}
token = tokenPromise().then(res => res);
return token;
I'd create a method called get token first.
Updated/simplified based off 4castle's suggestion
getToken() {
return fetch(tokenUrl, options)
.then(res => res.json())
.then(data => data.access_token)
}
Then inside the method you'd like to use the token
someMethod() {
getToken().then(token => {
// Use it.
})
}
Due to the api of a plugin I'm using not working properly. I need to merge the two different requests. I am using the thunk below.
I can get a response but I cannot seem to check for response.ok, and return the combined data:
export function fetchCategories() {
const firstPage =
"http://wordpress.rguc.co.uk/index.php/wp-json/tribe/events/v1/categories?per_page=60&page=1";
const secondPage =
"http://wordpress.rguc.co.uk/index.php/wp-json/tribe/events/v1/categories?per_page=60&page=2";
return dispatch => {
dispatch(isLoading(true));
Promise.all([fetch(firstPage), fetch(secondPage)])
.then(response => {
// check for ok here
response.ForEach(response => {
if (!response.ok) throw Error(response.statusText);
});
dispatch(isLoading(false));
return response;
})
.then(response => response.json())
// dispatch combined data here
.then(data => dispatch(fetchSuccessCategories(data)))
.catch(() => dispatch(hasErrored(true)));
};
}
Any ideas?
You are doing the check for .ok fine because it's in a loop, but your response is actually an array of two Response objects, it does not have a .json() method. You could do Promise.all(responses.map(r => r.json())), but I would recommend to write a helper function that does the complete promise chaining for one request and then call that twice:
function fetchPage(num) {
const url = "http://wordpress.rguc.co.uk/index.php/wp-json/tribe/events/v1/categories?per_page=60&page="+num;
return fetch(url).then(response => {
if (!response.ok)
throw new Error(response.statusText);
return response.json();
});
}
export function fetchCategories() {
return dispatch => {
dispatch(isLoading(true));
Promise.all([fetchPage(1), fetchPage(2)]).then(data => {
dispatch(isLoading(false));
dispatch(fetchSuccessCategories(merge(data)));
}, err => {
dispatch(isLoading(false));
dispatch(hasErrored(true));
});
};
}
I have a list of urls I wish to fetch. All of these urls returns a json object with a property valid. But only one of the fetch promises has the magic valid property to true.
I have tried various combinations of url.forEach(...) and Promises.all([urls]).then(...). Currently my setup is:
const urls = [
'https://testurl.com',
'https://anotherurl.com',
'https://athirdurl.com' // This is the valid one
];
export function validate(key) {
var result;
urls.forEach(function (url) {
result = fetch(`${url}/${key}/validate`)
.then((response) => response.json())
.then((json) => {
if (json.license.valid) {
return json;
} else {
Promise.reject(json);
}
});
});
return result;
}
The above is not working because of the async promises. How can I iterate my urls and return when the first valid == true is hit?
Let me throw a nice compact entry into the mix
It uses Promise.all, however every inner Promise will catch any errors and simply resolve to false in such a case, therefore Promise.all will never reject - any fetch that completes, but does not have license.valid will also resolve to false
The array Promise.all resolves is further processed, filtering out the false values, and returning the first (which from the questions description should be the ONLY) valid JSON response
const urls = [
'https://testurl.com',
'https://anotherurl.com',
'https://athirdurl.com' // This is the valid one
];
export function validate(key) {
return Promise.all(urls.map(url =>
fetch(`${url}/${key}/validate`)
.then(response => response.json())
.then(json => json.license && json.license.valid && json)
.catch(error => false)
))
.then(results => results.filter(result => !!result)[0] || Promise.reject('no matches found'));
}
Note that it's impossible for validate to return the result (see here for why). But it can return a promise for the result.
What you want is similar to Promise.race, but not quite the same (Promise.race would reject if one of the fetch promises rejected prior to another one resolving with valid = true). So just create a promise and resolve it when you get the first resolution with valid being true:
export function validate(key) {
return new Promise((resolve, reject) => {
let completed = 0;
const total = urls.length;
urls.forEach(url => {
fetch(`${url}/${key}/validate`)
.then((response) => {
const json = response.json();
if (json.license.valid) {
resolve(json);
} else {
if (++completed === total) {
// None of them had valid = true
reject();
}
}
})
.catch(() => {
if (++completed === total) {
// None of them had valid = true
reject();
}
});
});
});
}
Note the handling for the failing case.
Note that it's possible to factor out those two completed checks if you like:
export function validate(key) {
return new Promise((resolve, reject) => {
let completed = 0;
const total = urls.length;
urls.forEach(url => {
fetch(`${url}/${key}/validate`)
.then((response) => {
const json = response.json();
if (json.license.valid) {
resolve(json);
}
})
.catch(() => {
// Do nothing, converts to a resolution with `undefined`
})
.then(() => {
// Because of the above, effectively a "finally" (which we
// may get on Promises at some point)
if (++completed === total) {
// None of them had valid = true.
// Note that we come here even if we've already
// resolved the promise -- but that's okay(ish), a
// promise's resolution can't be changed after it's
// settled, so this would be a no-op in that case
reject();
}
});
});
});
}
This can be done using SynJS. Here is a working example:
var SynJS = require('synjs');
var fetchUrl = require('fetch').fetchUrl;
function fetch(context,url) {
console.log('fetching started:', url);
var result = {};
fetchUrl(url, function(error, meta, body){
result.done = true;
result.body = body;
result.finalUrl = meta.finalUrl;
console.log('fetching finished:', url);
SynJS.resume(context);
} );
return result;
}
function myFetches(modules, urls) {
for(var i=0; i<urls.length; i++) {
var res = modules.fetch(_synjsContext, urls[i]);
SynJS.wait(res.done);
if(res.finalUrl.indexOf('github')>=0) {
console.log('found correct one!', urls[i]);
break;
}
}
};
var modules = {
SynJS: SynJS,
fetch: fetch,
};
const urls = [
'http://www.google.com',
'http://www.yahoo.com',
'http://www.github.com', // This is the valid one
'http://www.wikipedia.com'
];
SynJS.run(myFetches,null,modules,urls,function () {
console.log('done');
});
It would produce following output:
fetching started: http://www.google.com
fetching finished: http://www.google.com
fetching started: http://www.yahoo.com
fetching finished: http://www.yahoo.com
fetching started: http://www.github.com
fetching finished: http://www.github.com
found correct one! http://www.github.com
done
If you want to avoid the fact of testing each URL, you can use the following code.
const urls = [
'https://testurl.com',
'https://anotherurl.com',
'https://athirdurl.com' // This is the valid one
];
export function validate(key) {
return new Promise((resolve, reject) => {
function testUrl(url) {
fetch(`${url}/${key}/validate`)
.then((response) => response.json())
.then((json) => {
if (json.license.valid) {
resolve(json);
return;
}
if (urlIndex === urls.length) {
reject("No matches found...");
return;
}
testUrl(urls[urlIndex++]);
});
}
let urlIndex = 0;
if (!urls.length)
return reject("No urls to test...");
testUrl(urls[urlIndex++]);
});
}