I developing my toy project with riot api.(league of legends game)
And I have a question. as you can see I request to riot api in for loop.
It take 20 seconds... this is the problem. I think riot api is not a problem.
I think just request in for loop is the problem.
How can I make it faster than now? help me please ㅠ.ㅠ
for (let cnt = 0; cnt < 20; cnt++) {
let temp = await api.getMatchInfo(res.matches[cnt].gameId);
if (temp.gameMode === "CLASSIC" && temp.gameDuration >= 800) {
console.log("gameList:", temp);
temp["cnt"] = cnt;
gameList.push(temp);
rankCnt++;
}
}
Instead of sequence request each for loop, you can make it parallel by Promise.all. You can see more here https://developer.mozilla.org/vi/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
const promises = []
for (let cnt = 0; cnt < 20; cnt++) {
promises.push(api.getMatchInfo(res.matches[cnt].gameId)
}
Promise.all(promises).then(games => {
console.log(games)
})
//or
const games = await Promise.all(promises)
console.log(games)
Related
Basically I'm trying to create a pagination limit per embed, So if there are 10+ values then there would be separate embeds per 10. So far I was doing, But it just spams it and doesn't work any ideas?
const pages = []
const result = []
const limit = 10;
console.log(result.length)
for (let i = 0; i < result.length; i++) {
if(limit >= result.length){
let reactionhistry = new Discord.MessageEmbed()
.setDescription(result)
pages.push(reactionhistry)
paginationEmbed(message, pages)
}
}
Building a Sort-Visualizer in React using the Create-React-App [https://roy-05.github.io/sort-visualizer/ ]
I'm animating each iteration of the loop using setTimeouts. On dev console I get the following warning:
Line 156:32: Function declared in a loop contains unsafe references to variable(s) 'minimum', 'minimum', 'minimum', 'minimum' no-loop-func
Here's the code-snippet:
for(let i=0; i<arr.length-1; i++){
let minimum = i; //Declare minimum here
setTimeout(()=>{
for(let j = i+1; j<arr.length; j++){
setTimeout(()=>{
//Getting a warning for these references:
array_bar[j].style.backgroundColor = 'red';
array_bar[minimum].style.backgroundColor = 'blue';
setTimeout(()=>{
if(arr[j] < arr[minimum]){
array_bar[minimum].style.backgroundColor = 'lightblue';
minimum = j;
}
else{
array_bar[j].style.backgroundColor = 'lightblue';
}
}, 4);
}, (j-1)*4);
}
Going through ESLint Docs, I believe the issue might be that i'm modifying the value inside the setTimeout but the variable is declared outside its scope.
I'm not sure how to fix that warning, any help will be appreciated!
Note: Here's the entire function if you need it -
selectionSort(){
const arr = this.state.array,
array_bar = document.getElementsByClassName("array-elem");
this.setState({startedSelectionSort: true});
for(let i=0; i<arr.length-1; i++){
let minimum = i; //Declare minimum here
setTimeout(()=>{
for(let j = i+1; j<arr.length; j++){
setTimeout(()=>{
//Getting a warning for these references:
array_bar[j].style.backgroundColor = 'red';
array_bar[minimum].style.backgroundColor = 'blue';
setTimeout(()=>{
if(arr[j] < arr[minimum]){
array_bar[minimum].style.backgroundColor = 'lightblue';
minimum = j;
}
else{
array_bar[j].style.backgroundColor = 'lightblue';
}
}, 4);
}, (j-1)*4);
}
setTimeout(()=>{
let temp = arr[i],
arr1_height = arr[minimum],
arr2_height = arr[i];
arr[i] = arr[minimum];
arr[minimum] = temp;
array_bar[i].style.height = `${arr1_height}px`;
array_bar[minimum].style.height = `${arr2_height}px`;
array_bar[i].style.backgroundColor = "green";
if(i !== minimum){
array_bar[minimum].style.backgroundColor = 'lightblue';
}
}, 400);
if(i === arr.length-2){
setTimeout(()=>{
array_bar[i+1].style.backgroundColor = "green";
},800);
}
}, i*400);
}
setTimeout(()=>{
this.setState({sorted: true})
}, arr.length*400+1750);
}
I also encountered same warning. In my case, I declared variable outside the iteration, but modified variable inside forEach method.
Something like:
// some code above
let validInputs = true;
someInputs.forEach( input => {
validInputs = input.value && validInputs;
})
After I did some reserch, I found in this post, JSHint error : Functions declared within loops referencing an outer scoped variable may lead to confusing semantics, mentioned that JSHint doesn't like how the anonymous function in there is being re-created over and over.
I changed forEach arrow function to for (let index i = 0; index < someInputs.length; index++), and the warning is gone.
Perhaps in your case, change setTimeout to traditional non-arrow function can remove the warning.
updated on Apr 7th 2021
As I'm reading the Professional JavaScript for Web Developers, 4th edition, I might have found why this warning is implemented in the ESLint.
From section 4.3 Garbage Collection sections, the book mentioned that closure might also lead to memory leak.
The purpose for forEach and arrow function is to limit the scope of the variable, as describes below from MDN:
Arrow functions establish "this" based on the scope the Arrow function is defined within. from Arrow function expressions
In section Creating closures in loops: A common mistake, MDN mentioned:
Another alternative could be to use forEach() to iterate over the helpText array and attach a listener to each , as shown:
function showHelp(help) {
document.getElementById('help').textContent = help;
}
function setupHelp() {
var helpText = [
{'id': 'email', 'help': 'Your e-mail address'},
{'id': 'name', 'help': 'Your full name'},
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];
helpText.forEach(function(text) {
document.getElementById(text.id).onfocus = function() {
showHelp(text.help);
}
});
}
setupHelp();
In our implementation, calling arrow functions inside forEach is creating closure of closure, which obviously can create some confusing semantics for garbage collection.
You're correct that modifying the variable inside setTimeout is causing the issue. You can get around this by wrapping setTimeout inside a promise and waiting for it to resolve before modifying your variables. This is much cleaner using async/await:
for (let i = 0; i < arr.length - 1; i++) {
let minimum = i;
await new Promise(resolve => setTimeout(resolve, i * 400));
for (let j = i + 1; j < arr.length; j++) {
array_bar[j].style.backgroundColor = "red";
array_bar[minimum].style.backgroundColor = "blue";
await new Promise(resolve => setTimeout(resolve, (j - 1) * 400));
if (arr[j] < arr[minimum]) {
array_bar[minimum].style.backgroundColor = "lightblue";
minimum = j;
}
}
}
With each loop, you're creating a promise that resolves once the timeout is expired. Using await will pause execution of your function until the promise resolves. Then, you can modify variables like minimum because they are no longer within the scope of the callback function you were originally passing into setTimeout.
Using typescript and React, I was able to initialize minimum inside of the for loop call, and then reinitialize once I got inside:
for (let i, minimum = 0; i < arr.length - 1; i++) {
minimum = i; //reinitialize minimum here
setTimeout(() => {
for (let j = i + 1; j < arr.length; j++) {
setTimeout(() => {
//Getting a warning for these references:
array_bar[j].style.backgroundColor = "red";
array_bar[minimum].style.backgroundColor = "blue";
setTimeout(() => {
if (arr[j] < arr[minimum]) {
array_bar[minimum].style.backgroundColor = "lightblue";
minimum = j;
} else {
array_bar[j].style.backgroundColor = "lightblue";
}
}, 4);
}, (j - 1) * 4);
}
});
}
For me redeclaring the variables in the timeout function did remove that warning for me in FirebaseFunctions.
setTimeout(async ()=> {
var NumberInst = await admin
.firestore()
.collection("CollName")
.doc('DocName')
.get();
var Numbers = NumberInst.data().postponeX;
}, 1000 * 60 * 11 );
I've been working on node.js for a short time and currently have the following problem: I would like to iterate through an array and send an http request for each element in the array. These requests should be executed every 5 seconds. In addition to this, this should be done max. 10 times.
That means every 5 seconds an http request should be sent for all elements in the array.
I have already tried a branched async for and for each loop, but I am not getting the desired result.
I know where the problem is with my code but can't find another solution.
It looks something like this:
// Set for loop
for (var i = 0; i <= 10; i++) {
setTimeout(function () {
// Iterate through every element within the array
sendItems.forEach(function (arrayItem) {
// Some code
request (.....)
// Exit condition
if (sendItems[sendItems.length - 1].status === 'Failed'|||
sendItems[sendItems.length - 1].status ==='Successful') {
if (cbFlag === false) {
interval = 0;
resolve(sendItems);
}
}
});
}interval * i);
Assuming by request you're referring to the nodejs module. Here a possible solution using something strange like a sleep :)
const request = require('request');
const _request = require('util').promisify(request);
let sendItems = [1,2,3];
let sleep = (ms) => {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(`waiting ${ms/1000} sec..`)
resolve();
}, ms);
})
}
(async function loop() {
for (let i = 0; i < 10; i++) {
for (let id of sendItems) {
let r = await _request('https://jsonplaceholder.typicode.com/posts/'+id, { json: true })
console.log(r.body.id);
}
await sleep(5000);
} console.log("ok that's it")
})();
I have a controller with a for loop that make's HEAD requests for an array of URLs to check if the file exists. When I get the response from the HEAD request, i need the index number from the array that the request was based on.
var allFiles = [], files = [];
allFiles.push({"url":"http://www.example.com/foo","source":"source1"});
allFiles.push({"url":"http://www.example.com/bar","source":"home"});
allFiles.push({"url":"http://www.example.com/wtf","source":"outer space"});
for(var i=0,len=allFiles.length;i<len;i++) {
$http.head(allFiles[i].url).then(function(response) {
files.push(allFiles[VALUE_OF_i_AT_TIME_OF_REQUEST]);
}
}
EDIT:
Because it is an asynchronous call, I cannot use i in place of VALUE_OF_i_AT_TIME_OF_REQUEST. Doing that results in i always being equal to len-1
I guess I can send the index number as data with the request and retrieve it from the response but for some reason that seems hack-ish to me.
Is there a better way?
You can do this with a function closure
for (var i = 0, len = allFiles.length; i < len; i++) {
function sendRequest(index) {
$http.head(allFiles[index].url).then(function (response) {
files.push(allFiles[index]);
});
}
sendRequest(i);
}
I may be oversimplifying this (asynchronous code is still tricky to me), but could you set i to a new local variable j on each loop then reference j instead of i in files.push(allFiles[j]):
var allFiles = [], files = [];
allFiles.push({"url":"http://www.example.com/foo","source":"source1"});
allFiles.push({"url":"http://www.example.com/bar","source":"home"});
allFiles.push({"url":"http://www.example.com/wtf","source":"outer space"});
for(var i = 0, len = allFiles.length; i < len; i++) {
var j = i;
$http.head(allFiles[i].url).then(function(response) {
files.push(allFiles[j]);
}
}
I did something similar to #rob's suggestion and it seems to be doing the trick.
var allFiles = [], files = [];
allFiles.push({"url":"http://www.example.com/foo","source":"source1"});
allFiles.push({"url":"http://www.example.com/bar","source":"home"});
allFiles.push({"url":"http://www.example.com/wtf","source":"outer space"});
for(var i=0,len=allFiles.length;i<len;i++) {
(function(i) {
$http.head(allFiles[i].url).then(function(response) {
files.push(allFiles[i]);
}
})(i);
}
I imagine this could be a pretty general problem, but in this case I'm using AngularJS and the SoundCloud API.
Here's the flow:
Call loadTracks()
loadTracks() should load the tracks of a SoundCloud user, 50 at a time, until the list runs out.
loadTracks() does this by calling another function, sc.getTracksByUser(id), which returns a promise
loadTracks() should update the variable $scope.tracks with each 50 track batch when it arrives
The SoundCloud API provides an option offset, so loading the batches is relatively easy. I think it's the promise that is tripping me up. Without the promise, the solution would be:
$scope.tracks = [];
var loadTracks = function() {
var page = -1,
done = false,
newTracks;
while (!done) {
newTracks = getFiftyTracks(page++);
for (var i = 0; i < newTracks.length; i++) {
$scope.tracks.push(newTracks[i]);
}
if (newTracks.length < 50) done = true;
}
}
Unfortunately, that line with getFiftyTracks in it is not how it works. The actual implementation (using a promise) is:
sc.getTracksByUser(id).then(function (response) {
for (var i = 0; i < response.length; i++) {
$scope.tracks.push(response[i]);
}
}
I'm guessing the solution to this is some sort of recursion, but I'm not sure.
You can do that in this way
sc.getTracksByUser(id).then(function (response) {
for (var i = 0; i < response.length; i++) {
$scope.tracks.push(response[i]);
}
// if response return 50 track call getTracksByUser again
if (response.length === 50) sc.getTracksByUser(id);
});