Why is my search function so slow + buggy sometimes? - javascript

I originally had only one URL to fetch data, and my app worked flawlessly. But then I realized I had to use two different URLs to fetch the data I needed. So I changed the code a little bit. Everything is pretty much the same except for the getData function, where I used map to iterate the list of URLs and fetch the data. My app is really buggy now.
Problems:
When I search for a streamer, it sometimes never renders the result on the page. And the message "search for your favorite streamer!" doesn't disappear even though this.state.value is now NOT equal to ' '. But when I change the input, it gets rendered out of nowhere.
It sometimes fails to render the data fetched and I get the FinderResultContainer rendered with no data in it. (No name, no img, no offline/online.)
What is exactly happening here? I feel like it is because I am not using the component life cycle methods. If so, then what exactly do I need to do? I am a total beginner, so I need to some guidance.
getData(value) {
let streamerData = [];
let streamerInfo = {};
let urls = ['https://wind-bow.gomix.me/twitch-api/streams/' + value, 'https://wind-bow.gomix.me/twitch-api/users/' + value];
urls.map(function(each, index) {
fetch(each).then((response) =>
response.json()
).then((streamer) => streamerData.push(streamer)).then(()=>streamerInfo[index]=streamerData[index]).then(()=>{if (index === urls.length-1){this.setState({streamer: streamerData})}})},this)
}
https://codepen.io/brood915/pen/OWQpmy

fetch is asynchronous and returns a promise, so you don't know exactly when it'll be done. You can use Promise.all() to wait every promise to resolve and then work with the data (note that all promises will execute at the same time, so if you have a large list of urls you will be sending tons of request per second).
Edit: I saw your pen and simplified the code, this works fine to me.
No blank component here. Note that you only need to use then when returning promises.
getData(value, type) {
let urls = [
'https://wind-bow.gomix.me/twitch-api/streams/' + value,
'https://wind-bow.gomix.me/twitch-api/users/' + value
];
const getJson = url => fetch(url).then(res => res.json());
Promise.all(urls.map(getJson))
.then(streamer => {
if (type === "search") {
this.setState({ streamer });
console.log("searching")
} else {
console.log("displaying");
this.getStreamer(streamer[0], streamer[1]);
}
}).catch(err => {
console.log('Something went wrong...')
})
}

Related

Javascript : issues with Promise.all and json() being logged as "not a function"

I'm having some issues with the "Promise.all" method.
Bascially, I have an array of URL (here is a simple one if you guys want to test :
const urlArray = [
"https://coverartarchive.org/release/985adeec-a1fd-4e79-899d-10c54b6af299",
"https://coverartarchive.org/release/4c54ee58-86df-3ba5-aaad-6b284293141b",
"https://coverartarchive.org/release/cd8e5736-ec8c-3c4d-a231-ac097877d87a",
"https://coverartarchive.org/release/b9b7641f-9389-342e-8be9-e463bd52fdb9",
"https://coverartarchive.org/release/b6206cad-15eb-3a95-b67e-1f49849e5fbd",
"https://coverartarchive.org/release/db425753-965f-4881-955b-8cd3ef65d1e6",
"https://coverartarchive.org/release/fa4f230a-e78c-32a8-bec8-3a7425aba9d2",
"https://coverartarchive.org/release/fa023617-1585-4ae6-81b6-1a07c47ecb2a",
"https://coverartarchive.org/release/61782e1c-67a2-487c-8324-6431c628cad8",
"https://coverartarchive.org/release/b16e94f3-ad3b-4e3b-9fad-0ef3d2a0958e",
"https://coverartarchive.org/release/37e7091f-9ebc-4ac8-875b-5c88f7e5fba8",
"https://coverartarchive.org/release/a63b6cc9-899c-447d-b0e6-d1e439379eb2",
"https://coverartarchive.org/release/d2d3df43-65c4-499e-90d2-22a157cc7dea",
"https://coverartarchive.org/release/9cb95cac-4a0d-4fbb-9237-544a99f29b57",
"https://coverartarchive.org/release/7cf87b52-47e3-4d12-8890-53a910792b70"
]
Normally, when a promise is resolved, it should return a JSON object, has seen when you enter one of those URLs above in your browser with infos about cover arts for an album release.
So I have tried using Promise.all using this array and see what goes, but I simply can't get something to work : the json() method. I have tried several ways to handle this, found on stackoverflow or elsewhere on the internet, I just always get the "Uncaught (in promise) TypeError: response.json is not a function"
Here are several things I tried :
Promise.all(urlArray)
.then(toJSON)
.then((jsonObjects) => console.log(jsonObjects));
function toJSON(responses) {
if (!Array.isArray(responses)) {
// also handle the non array case
responses = [responses];
}
return Promise.all(responses.map((response) => response.json()));
}
Same thing I guess but without helper
Promise.all(urlArray)
.then((res) => {
const responses = res.map((response) => response.json());
return Promise.all(responses);
})
.then((data) => console.log(data));
The worst thing is, I did manage to do it a few days ago, then changed my mind about how to go with this, and I just can't find how I solved this the first time around. If you guys have any idea what I'm doing wrong, don't hesitate to point it out and scold me about it !
Cheers
Your urlArray is an array of plain strings, not an array of requests. You never actually make any network requests in your code - you don't have any Promises.
Map the array of request URLs to an array of Promises first.
const urlArray = [
"https://coverartarchive.org/release/985adeec-a1fd-4e79-899d-10c54b6af299",
"https://coverartarchive.org/release/4c54ee58-86df-3ba5-aaad-6b284293141b",
"https://coverartarchive.org/release/cd8e5736-ec8c-3c4d-a231-ac097877d87a",
"https://coverartarchive.org/release/b9b7641f-9389-342e-8be9-e463bd52fdb9",
"https://coverartarchive.org/release/b6206cad-15eb-3a95-b67e-1f49849e5fbd",
"https://coverartarchive.org/release/db425753-965f-4881-955b-8cd3ef65d1e6",
"https://coverartarchive.org/release/fa4f230a-e78c-32a8-bec8-3a7425aba9d2",
"https://coverartarchive.org/release/fa023617-1585-4ae6-81b6-1a07c47ecb2a",
"https://coverartarchive.org/release/61782e1c-67a2-487c-8324-6431c628cad8",
"https://coverartarchive.org/release/b16e94f3-ad3b-4e3b-9fad-0ef3d2a0958e",
"https://coverartarchive.org/release/37e7091f-9ebc-4ac8-875b-5c88f7e5fba8",
"https://coverartarchive.org/release/a63b6cc9-899c-447d-b0e6-d1e439379eb2",
"https://coverartarchive.org/release/d2d3df43-65c4-499e-90d2-22a157cc7dea",
"https://coverartarchive.org/release/9cb95cac-4a0d-4fbb-9237-544a99f29b57",
"https://coverartarchive.org/release/7cf87b52-47e3-4d12-8890-53a910792b70"
]
Promise.all(
urlArray.map(
url => fetch(url).then(res => res.json())
)
)
.then((results) => {
console.log('got all results');
// use results here
});

Fetch Function Not Being Performing In Order | Discord.js

The summary:
When I run this function, it runs once, skipping over my methods, and then runs again in the proper order.
The code:
function fetch_messages(searched_channel_id){
client.channels.cache.get(game_log_channel).messages.fetch({ limit: 10 })
.then(messages => {
keys = Array.from(messages.keys());
console.log(keys);
for ( var i=0; i < keys.length; i++){
console.log('iterating through messages');
client.channels.cache.get(game_log_channel).messages.fetch(keys[i])
.then(msg => {
var msgContent = msg.content;
if (msgContent === undefined){
msgContent = 'undefined';
console.log('undefined message');
}
else if (msgContent.includes(searched_channel_id)){
console.log('channel record found in logs');
return [ msg, msgContent, msg.id ];
}
});
}
});
return 'not found';
};
The console log:
The problems:
Main problem is the fact that it is occurring... is there any way to rewrite this which does not reproduce the error?
Secondary problem is why it occurs. I have not been able to find any documentation of this kind of thing happening and am curious as to the origin.
Just think about it. fetch() returns a promise. When you call the fetch() method, knowing that it's asynchronous, you're basically saying that
fetch the message with the ID of keys[i] -- no matter how long it
takes -- but please WHEN it's fetched (i.e. the promise is resolved),
check if the fetched message's content includes the
searched_channel_id. Cheers.
Notice the "WHEN" part. Anything inside the then() method is run after the fetch is resolved (an HTTP request is sent, data received, converted, etc.). You don't know how long it takes.
Anyway... as you're in a for loop, it's a bit different now. This time it's like
I've got keys.length IDs. fetch me all these messages where the ID
will be keys[i]. No matter how long it takes, but please WHEN a
message is fetched, check if the fetched message's content includes
the searched_channel_id. I know the order you start fetching them,
and I can understand that it can take different times to finish getting
the results back. You don't have to fetch one after the other as I
don't really care the order you finish fetching them.
So, your code starts resolving these promises in the order of key[0], key[1], etc. but you can't control when it's finished without waiting for the previous ones to finish.
And then there is that return 'not found' at the end. That's like saying
do these all I mentioned above but, you know what, I don't really care
if you will found that message, let's just say it's not found. I'm too
lazy to wait for you to finish all those. You can still finish the job
I told you but meh...
You could update your code and use the collection#find() method to find an item where the given function returns a truthy value:
async function findMessage(channelId) {
const gameLogChannel = client.channels.cache.get(gameLogChannel);
const logMessages = await gameLogChannel.messages.fetch({ limit: 10 });
const foundMessage = logMessages.find((msg) =>
msg.content.includes(channelId),
);
return foundMessage || 'not found';
}
findMessage('98573472597130')
.then(console.log);
I recommend you to watch "What the heck is the event loop anyway? | Philip Roberts | JSConf EU"

Iterate through Paginated API Results in Javascript

I'm writing a function in Javascript that makes a fetch and receives results that are paginated (as expected, the results are long).
The results will contain a "next_page" which is the fetch home url for the next page of results.Ideally I would like to loop and constantly fetch until I reach the end of the results aka when "next_page" = null.
I can't seem to figure out how to loop through the results while next_page isn't null. What seems to happen is I get stuck in an infinite while loop.
Any suggestions welcome. I've provided pseudo code below.
while(next_page!=null){
fetch(apiUrl)
.then(res=>res.json())
.then(data => {
apiUrl=data["next_page]
}
if(apiUrl == null)
{
res.send(data)
break;
}
}
I was thinking the while loop would let me iterate until there was no next_page (aka when it's null). Seems like it's just infinite looping without ever hitting the fetch because the apiUrl doesn't get set to null.
You can try below function, instead of while loop
// Initial API Call
fetchData('http://localhost/test1.php?page=1');
// Create the function for API Call
function fetchData(apiUrl){
fetch(apiUrl)
.then(res=>res.json())
.then(data => {
console.log(data);
apiUrl = data['next_page'];
// Check next API url is empty or not, if not empty call the above function
if(apiUrl != '' && apiUrl != null){
fetchData(apiUrl);
}
})
}
This is probably because you're reading data["next_page"] to apiUrl, but then checking a different variable in the loop. Should be while(apiUrl) { ... instead.

Promise.all() with map, silenced

actually I'm facing a problem with Promises, a miss understanding surely.
I have an array with a list of ids that I use to make an API call that I'm sure it's valid and working. So I have to make foreach element in this list an API call => Promise.
I read about promises and I find this clean way to make it :
clean(){
/** Here, I'm just cleaning invalid ids to avoid a silenced map. I tried the
whole output in the API explorator, and it works well*/
this.facebook.objectIdsToDelete.forEach(element => {
let index = this.facebook.objectIdsToFilter.indexOf(element);
this.facebook.objectIdsToFilter.splice(index, 1);
});
Promise.all(this.facebook.objectIdsToFilter.map(value=>{
return this.facebook.callApiLikes(value);
}))
.then((responses) => {
this.debogger.debogSomething(responses);
})
.catch((reason) => {
this.debogger.debogSomething(reason);
})
}
debogger => it's a popup to show object content.
this.facebook.objecIdsToFilter => is a string[]
This popup do not raise, nothing happens.
Where I'm doing wrong ?

Sleep function for React-Native?

So I'm trying to fetch all 'places' given some location in React Native via the Google Places API. The problem is that after making the first call to the API, Google only returns 20 entries, and then returns a next_page_token, to be appended to the same API call url. So, I make another request to get the next 20 locations right after, but there is a small delay (1-3 seconds) until the token actually becomes valid, so my request errors.
I tried doing:
this.setTimeout(() => {this.setState({timePassed: true})}, 3000);
But it's completely ignored by the app...any suggestions?
Update
I do this in my componentWillMount function (after defining the variables of course), and call the setTimeout right after this line.
axios.get(baseUrl)
.then((response) => {
this.setState({places: response.data.results, nextPageToken: response.data.next_page_token });
});
What I understood is that you are trying to make a fetch based on the result of another fetch. So, your solution is to use a TimeOut to guess when the request will finish and then do another request, right ?
If yes, maybe this isn't the best solution to your problem. But the following code is how I do to use timeouts:
// Without "this"
setTimeout(someMethod,
2000
)
The approach I would take is to wait until the fetch finishes, then I would use the callback to the same fetch again with different parameters, in your case, the nextPageToken. I do this using the ES7 async & await syntax.
// Remember to add some stop condition on this recursive method.
async fetchData(nextPageToken){
try {
var result = await fetch(URL)
// Do whatever you want with this result, including getting the next token or updating the UI (via setting the State)
fetchData(result.nextPageToken)
} catch(e){
// Show an error message
}
}
If I misunderstood something or you have any questions, feel free to ask!
I hope it helps.
try this it worked for me:
async componentDidMount() {
const data = await this.performTimeConsumingTask();
if (data !== null) {
// alert('Moved to next Screen here');
this.props.navigator.push({
screen:"Project1.AuthScreen"})
}
}
performTimeConsumingTask = async() => {
return new Promise((resolve) =>
setTimeout(
() => { resolve('result') },
3000
)
);
}

Categories