i am trying to make a simple app that calls an api and the renders 10 images per page.
the first page loads but does not show images but the second page does.
what am i doing wrong?
let imageData = [];
fetch({api}
).then(res => res.json())
.then((data) => {
imageData.push(...data.results)
})
fetch({api}
).then(res => res.json())
.then((data) => {
imageData.push(...data.results)
})
let currentPage = 1;
let imagesPerPage = 10;
const changePage = (page)=> {
let nextBttn = document.getElementById("nextBttn");
let prevBttn = document.getElementById("prevBttn");
let root = document.getElementById("root");
let pageCount = document.getElementById("page");
if (page < 1) page = 1;
if (page > numPages()) page = numPages();
root.innerHTML = "";
for (var i = (page - 1) * imagesPerPage; i < (page * imagesPerPage) && i < imageData.length; i++) {
const createImage = document.createElement('img')
createImage.src = imageData[i].urls.thumb
createImage.setAttribute('id',imageData[i].id)
root.appendChild(createImage)
}
pageCount.innerHTML = page + "/" + numPages();
window.onload = ()=>{
changePage(1);
};
there are two fetches because it returns 30 images and i need 60
The problem is you have two (three technically) asynchronous tasks running
that depend on one another, but without any code to synchronize them back up.
Here's the possible order of events:
You initiate a fetch of the first 30 images
You initiate a fetch of the second 30 images
No matter how fast these requests are, their callback won't fire until the rest of this code is parsed/executed.
You set a callback for Page Load
Here's where things can get wonky.
Scenario A (unlikely, you wouldn't have an error):
The server is fast as heck (or cached response) and already has a response waiting for you. In theory, I believe its possible the fetch callback fires before the page load (though I could be wrong here). In this unlikely scenario, the response data is loaded into the imageData. Then the page load event fires, and calls changePage, which displays the images from imageData.
Scenario B (most likely):
The server takes some milliseconds to respond but the page elements are all created and therefore onLoad callback fires first. It attempts to display the imageData (but there isn't any yet). The server finally responds with the 60 images. No code is executed that tells the webpage to draw this new image data.
As you can see, because your code assumes the image data is already available when it tries to display some images on page load (not data load), it fails when the image data takes awhile to return and upon returning does not notify the page to display the new image data.
Here's how you can modify it:
let response1 = fetch({api})
.then(res => res.json())
.then((data) => {
imageData.push(...data.results)
});
let response2 = fetch({api})
.then(res => res.json())
.then((data) => {
imageData.push(...data.results)
});
Promise.all([response1, response2])
.then(() => changePage(1));
// Remove onLoad callback because we don't really care when the page loads, we care when the data loads.
Related
I have being stuck with this code more than 1 week. I still cannot make it work as it should.
I want to receive data from an external Api, but I have two issues that I am not sure how to handle:
1- The URl of my api call is from different users so I need to change the url base on the user's ID and make an API call per user.
I want to send the data received from the external API to an array and if the user's ID is inside the data I just want to update the data, if it is not in the array just insert the data.
2- This process of calling the api needs to be repeat every 20 seconds to receive the update information.
Here is the code I am trying to fix:
This is the code in my backend/server.I am using Node JS Express.
let userData = [];
async function callApi() {
for( let i = 80; i < 82; i++ ){
const url = "https://myurl.com/user/" + i;
//Making the APi call
const response = await fetch(url);
const information = await response.json();
let check = userData.find(obj => obj.data.userId === information.data.userId);
if (check == undefined) {
userData.push(information);
} else{
const index = userData.findIndex( x => x.data.userid === information.data.userId);
userData[index] = information;
}
}
callApi();
//Repeat the api call every 20 seconds
setInterval(function(){
callApi();
}, 20000);
// free endpoint
app.get("/free-endpoint", (req, res) => {
res.json(userData);
});
**In my frontend: **
I want to make a https request to my server url/free-endpoint and get the updated data from userData every 20 seconds.
I hope you can help me!
Thank you
I have try setInterval inside my route
app.get("/free-endpoint", (req, res) => {
res.json(userData);
});
but it always appear an error that I cannot send the header more than one time
I'm learning to use calls on APIs and I'm trying to get my API response to not return the same data over and over again, currently, it does despite using setInterval, I have tried changing the order and making it async but am currently not able to figure out how to change this.
The idea is to make an original call to the API on page load, then after 6 or so seconds, make the call again but change the response automatically hence the setInterval.
Here is my code:
const advice = document.getElementById("advice");
const adviceNum = document.getElementById("adviceNum");
const slip_id = Math.random() * 100;
console.log(slip_id)
fetch(`https://api.adviceslip.com/advice/${slip_id}`)
.then(response => {
return response.json();
})
.catch(error => {
console.log(error);
})
.then(data => {
console.log(data);
const returnedAdvice = data.slip.advice;
console.log(returnedAdvice);
const idAdvice = data.slip.id;
adviceNum.innerText = `ADVICE #${idAdvice}`;
advice.innerText = `"${returnedAdvice}"`;
setInterval(() => {
console.log(data);
const returnedAdvice = data.slip.advice;
console.log(returnedAdvice);
const idAdvice = data.slip.id;
adviceNum.innerText = `ADVICE #${idAdvice}`;
advice.innerText = `"${returnedAdvice}"`;
}, 8000)
})
Would appreciate any help on what I'm doing wrong here!
Currently you're making a fetch request and when the data returns from that single request; you console.log etc.
Instead try the following, wrap your fetch request in the setInterval
setInterval(async function(){
const data = await fetch(`https://api.adviceslip.com/advice/${slip_id}`);
// check your response
// compute your values
// add them to the dom
}, 8000);
I am sending chained Fetch requests. First, I retrieve data from database and request pictures related to every title I got.
The HTML code won't be loaded to results div before image requests are sent. So it takes long time to see articles. How can I make the text to load before image requests starting to be sent?
async function getArticles(params) {
url = 'http://127.0.0.1:8000/articles/api/?'
url2 = 'https://api.unsplash.com/search/photos?client_id=XXX&content_filter=high&orientation=landscape&per_page=1&query='
const article = await fetch(url + params).then(response => response.json());
const cards = await Promise.all(article.results.map(async result => {
try {
let image = await fetch(url2 + result.title).then(response => response.json())
let card = // Creating HTML code by using database info and Splash images
return card
} catch {
let card = // Creating HTML code by using info and fallback images from database
return card
}
}))
document.getElementById('results').innerHTML = cards.join("")
};
I have tried using them separately but I was getting Promise Object.
If you don't want to wait for all the fetches, use an ordinary for loop and await each one sequentially.
async function getArticles(params) {
url = 'http://127.0.0.1:8000/articles/api/?'
url2 = 'https://api.unsplash.com/search/photos?client_id=XXX&content_filter=high&orientation=landscape&per_page=1&query='
const article = await fetch(url + params).then(response => response.json());
for (let i = 0; i < article.results.length; i++) {
let result = article.results[i];
let card;
try {
let image = await fetch(url2 + result.title).then(response => response.json())
card = // Creating HTML code by using database info and Splash images
} catch {
card = // Creating HTML code by using info and fallback images from database
}
document.getElementById('results').innerHTML += card;
}
}
However, this will be slower because it won't start each fetch until the previous one completes.
It's hard to run all the fetches concurrently but display the results in the order that they were sent, rather than the order that the responses were received. You could do it by creating a container DIV for each response before sending, then filling in the appropriate DIV when its response is received.
I want to fetch more than 100 image from an array of URLs. I checked every URL individually all are good. But when I fetch it from my code for some URLs I got this error
GET https://image-url net::ERR_HTTP2_PROTOCOL_ERROR 200
Here is my code
var imageUrls = [//contains more than 100 urls];
Promise.all(imageUrls.map(image => {
fetch(image).then(res => {
//this console log gives every response correctly
console.log(res.blob());
});
})).then(imageBlobs => {
//do something here
});
I also tried this
var imageUrls = [//contains more than 100 urls];
let counter = 0;
imageUrls.forEach(function(image, i){
let imageBlob = fetch(image).then(res => res.blob());
console.log(imageBlob);
counter ++;
if(counter == imageUrls.length){
// do something
}
});
Also this
var imageUrls = [//contains more than 100 urls];
Promise.all(imageUrls.map(image => {
console.log(fetchImage(image));
})).then(imageBlobs => {
//do something here
});
async function fetchImage(url){
return await fetch(url).then(res => res.blob());
}
But in this code I am also getting TypeError : failed to fetch.
I think this may be some kind of looping issue because loop executes fast but fetch() could not handle URLs that fast.
Can someone tell me how I can resolve this issue ?
I found some flaws in your code:
Code snippet 1:
By using console.log(res.blob()); you are consuming the response body and you do not pass it to the next ".then()". Also, res.blob() is an async function. So you have to await it or use .then() on it!
Code snippet 2:
Same as in 1 and also this line
let imageBlob = fetch(image).then(res => res.blob());
should be this let imageBlob = await fetch(image).then(res => res.blob());. Also, by using .forEach() you are fetching all 100 images at the same time! You are executing an asynchronous function in a synchronous loop while not awaiting for it to end.
Code snippet 3:
Same as in 1. console.log(fetchImage(image)); should be at least console.log(await fetchImage(image)); or since you are passing it to the next .then it should be return fetchImage(image);
EDIT: Could it be that you are getting CORS errors? I just saw a thread that had a similar problem.
I've been trying to figure out how Promises work with a rather simple example: one that fetches a number of images, loads it onto the page in order, counts the number of images loaded.
const addImg = url => {
fetch(url)
.then(validateResponse)
.then(readResponseAsBlob)
.then(showImage)
.catch(Error);
}
function showImage(responseAsBlob) {
const container = document.getElementById('img-container');
const imgElem = document.createElement('img');
container.appendChild(imgElem);
const imgUrl = URL.createObjectURL(responseAsBlob);
imgElem.src = imgUrl;
return imgUrl;
}
document.getElementById("add").onclick = () => {
document.getElementById("status").innerHTML = "Fetching...";
Promise.all(urls.map(url => addImg(url)))
.then(setTimeout(() => {
document.getElementById("status").innerHTML = document.getElementsByTagName("img").length + " images";
}, 0));
}
The addImg function fetches an image from the url, processes it as a blob and showImage renders adds a new img. When I try to add images from an array of urls, I have noticed a few problems I want to fix:
The images don't necessarily show up in order
the img count is not accurate
My first thought: if I deconstruct the addImg function so that it execute each step as a separate promise( fetch all -> then validate all -> then ... so on), it might work the way I intend it to, but I'm not sure if that's the right approach to it.
It might make more sense to you if you rewrote your code using async/await. If you rewrote your AJAX call as
const addImg = url => fetch(url)
.then(validateResponse)
.then(readResponseAsBlob)
.then(showImage)
.catch(Error);
And then you could do something like:
async function loadImages(){
for(image in imageList){
await addImg(image.url);
}
console.log('Images loaded');
}
This way your code will wait for each image load to complete before the next. Note that this isn't very performant but if you want them loading specifically in order then this is one you could achieve that easily.