External variable access when chaining Promises [duplicate] - promise

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 4 years ago.
I have a big problem in JS.
I have a function where I make a promise chain. A promise is "added" to the last promise in a for loop. Inside this loop there are some variables. Each function of each then needs to access these variables, that is, their values corresponding to the correct for iteration.
The problem is, I believe, since the promise is executed when the functions ends, each promise will read the same values of those variables (which have the values of the last for iteration).
I don't want this to happen. How can I do to solve it?
I have written a code that mimics my problem:
function test() {
var p = Promise.resolve();
for (var i = 0; i < 10; i++) {
var j = i * 10;
p = p.then(function() {
alert(j);
});
}
p.then(function() {
alert('finished');
})
}
test();
As you can see, each time the function inside then fires, it always reads value 90 of j, instead of reading all the correct values.
Thank you for your help

Change var to let and then each invocation of the loop will have its own set of variables.
function test() {
let p = Promise.resolve();
for (let i = 0; i < 10; i++) {
let j = i * 10;
p = p.then(function() {
console.log(j);
});
}
p.then(function() {
console.log('finished');
})
}
test();
let is block scoped so each block (in this case each iteration of the for loop) gets its own version of the i and j variables. So, when you do your alert(j), it will be using the correct variable.
FYI, I changed alert(j) to console.log(j) because using alert() can mess with asynchronous timing (since it blocks JS execution) whereas console.log() just reports and lets the code keep running so you get a better picture of how things actually run with console.log().

Related

For loop printing out the same result

Why does this javascript code output the same result?
var myAlerts = [];
for (var i = 0; i < 5; i++) {
myAlerts.push(
function inner() {
alert(i);
}
);
}
myAlerts[0](); // 5
myAlerts[1](); // 5
myAlerts[2](); // 5
myAlerts[3](); // 5
myAlerts[4](); // 5
I'd expect to see in 1, 2, 3, 4. It feels like it's something related to lexical scoping, but what's the real reason behind?
Can someone explain exactly how this piece of code work behind the scenes?
This will produce the expected results.
var myAlerts = [];
for (var i = 0; i < 5; i++) {
myAlerts.push(alert(i));
}
myAlerts[0](); // 5
myAlerts[1](); // 5
myAlerts[2](); // 5
myAlerts[3](); // 5
myAlerts[4]();
By using the function inner() i is set to the same variable once it is called outside of your loop
let allows you to declare variables that are limited in scope to the
block, statement, or expression on which it is used. This is unlike
the var keyword, which defines a variable globally, or locally to an
entire function regardless of block scope. An explanation of why the
name "let" was chosen can be found here.
You can also use:
var myAlerts = [];
for (let i = 0; i < 5; i++) {
myAlerts.push(
function inner(){
alert(i)
});
}
myAlerts[0](); // 5
myAlerts[1](); // 5
myAlerts[2](); // 5
myAlerts[3](); // 5
myAlerts[4]();

Apps Script Exceeded MAXIMUM_RUNNING_TIME Workaround

First of all, I am continuing an old thread at this link that I am unable to comment on due to being a newbie.
I have a situation that an answer in that thread given by user Br. Sayan would really improve my Spreadsheet Google App Script. I am making calls to Google Url Shortener API, which puts quotas at 1 call per user per second. I have slowed my script down enough to accommodate this quota, but I then I run over the MAX_RUNNING_TIME for App Scripts execution due to the extended number of calls I need to make, so I need to break the loop when the execution time is exceeded and pick up where I left off.
Here is the code of his answer:
function runMe() {
var startTime= (new Date()).getTime();
//do some work here
var scriptProperties = PropertiesService.getScriptProperties();
var startRow= scriptProperties.getProperty('start_row');
for(var ii = startRow; ii <= size; ii++) {
var currTime = (new Date()).getTime();
if(currTime - startTime >= MAX_RUNNING_TIME) {
scriptProperties.setProperty("start_row", ii);
ScriptApp.newTrigger("runMe")
.timeBased()
.at(new Date(currTime+REASONABLE_TIME_TO_WAIT))
.create();
break;
} else {
doSomeWork();
}
}
//do some more work here
}
My Questions:
Is MAX_RUNNING_TIME a global variable with a value set by Apps Script that I can leave that reference as-is, or must I replace it with a value equalling the 6 minutes listed as the quota for run time on the Google API Console?
How can I place the bulk of my function within this script so that a loop that runs inside my function (say var i = 0; i < data.length; i++) will be synchronized with the loop in the portion given in the above code?
Clarification: when i is incremented up by 1, I need ii to increment by 1.
Does this happen automatically? Do I need one loop nested inside the other? Does the bulk of my function go in the first '//do some work here' or the second '//do some work here' or possibly even doSomeWork()?
#tehhowch agreed! However, HOW I need to adapt my code depends on where I need to put it in the above snippet.
Here is what I have so far:
'function short() {
var = startTime = (new Date()).getTime();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var run = 0;
var finc = 50;
var istart = run * finc;
var iLen = (run + 1) * finc;
var startRow = 2 + istart;
var endRow = startRow + finc;
var data = sheet.getSheetValues(startRow,2,endRow,1);
var shortUrl = new Array();
for (var i=istart; i < iLen; i++) {
Utilities.sleep(1100);
var url = UrlShortener.Url.insert({longUrl: data[i][0]});
shortUrl.push([url.id]);
Logger.log([url.id]);
}
var t = ss.setActiveSheet(ss.getSheets()[0]);
t.getRange(startRow,4,finc,data[0].length).clearContent();
t.getRange(startRow,4,finc,data[0].length).setValues(shortUrl);'
So if I update the code after each subsequent run to manually increase the variable 'run' by 1, and manually run the code again, this works.
I have also tried break it down into multiple functions by updating the i= and i < parts for each subsequent function, which also works, but requires much more manual work.
I have also tried, unsuccessfully, to use a prompt with a button press that continues the function, which would be better than the other attempts, but would still require a button press to resume the code after each run.
I want to automate the function as much as possible.

p5.play counter not working

I'm trying to write a program in which the end screen of the game only shows up after the last animation finishes. I'm using a counter that's implemented after each object is removed (which is only after it finishes its animation), and when that counter gets to zero, it should show the end screen. Unfortunately, from what I can tell, the counter statement isn't registering at all. I've inserted a print statement that isn't functioning.
var star;
var score;
var counter;
function setup() {
createCanvas(600,400);
score = 0;
counter = 20;
for (var s = 0; s < 20; s++) {
star = createSprite(random(width), random(height));
star.addAnimation("idle", idleAnim);
star.addAnimation("explode", explAnim);
star.changeAnimation("idle");
star.onMousePressed = function() {
this.changeAnimation("explode");
this.animation.looping = false;
score +=1
if (this.getAnimationLabel() == "explode" && this.animation.getFrame() == this.animation.getLastFrame()) {
this.remove();
counter -= 1;
print(counter);
}
}
}
}
function draw() {
if (score == 20 && counter == 0) {
background(255,222,51)
textSize(90);
fill(0)
text("YOU WIN!",95,225)
} else {
drawSprites();
}
}
You need to take a step back and debug your program. For example, are you sure the star.onMousePressed() function is firing? Are you sure the if statement is working the way you expected? Are you sure the player.dir() function is being called?
It sounds like your if statement is not being entered. can you find out the value of everything on that line? Which thing has a different value from what you expected?
Use console.log() statements, or use the JavaScript debugger, to answer all of the above. Figure out exactly which line of code is behaving differently from what you expected, and then isolate that problem in a MCVE. Good luck.

OpenCL possible reason a clGetEventInfo would cause a segfault?

I have a pretty complicated OpenCL app. It fires up 5 different contexts on 5 different GPUs, and executes the same kernel on all of them, splitting up the work into 1024 "chunks" to be processed.
Each time a kernel finishes, a result is checked for, and it's given a new chunk. Sometimes, when running, as the app is starting (very rarely mid-run) it will immediately segfault on the GetEventInfo call.
This is done in a loop using callbacks and clGetEventInfo calls to ensure something is finished before moving on to the next step.
GDB output:
(gdb) back
#0 0x00007fdc686ab525 in clGetEventInfo () from /usr/lib/libOpenCL.so.1
#1 0x00000000004018c1 in ready (event=0x26a00000267) at gputest.c:165
#2 0x0000000000404b5a in main (argc=9, argv=0x7fffdfe3b268) at gputest.c:544
The ready function:
int ready(cl_event event) {
int rdy;
if(!event)
return 0;
clGetEventInfo(event, CL_EVENT_COMMAND_EXECUTION_STATUS, sizeof(cl_int), &rdy, NULL);
if(rdy == CL_COMPLETE)
return 1;
return 0;
}
How the kernel is run, the event set, and checked. Some pseudocode inserted for brevity:
while(test if loop is complete) {
for(j = 0; j < GPUS; j++) {
if(gpu[j].waiting && loops < 9999) {
gpu[j].waiting = 0;
offset[j] = loops * 1024 * 1024;
loops++;
EC("kernel init", clEnqueueNDRangeKernel(queues[j], kernel_init[j], 1, &(offset[j]), &global_work_size, &work128, 0, NULL, &events[j]));
gpu[j].readsearch = events[j];
gpu[j].reading = 1;
}
}
for(j = 0; j < GPUS; j++) {
if(gpu[j].reading && ready(gpu[j].readsearch)) {
gpu[j].reading = 0;
gpu[j].waiting = 1;
// unrelated reporting other code here
}
}
}
Its pretty simple. There is more to the code, but it's unrelated. The ready/checking function is very simple. I even added debugging to the ready function to printf the event # to see what was happening when it crashed - nothing really. No pattern I could see.
What could be causing this?
Ugh. Found the problem. Since you cannot initialize values when you create/declare a struct, I was using some values uninitialized. I malloc'ed the gpu structs then just started using them. With if(gpu[x].reading &&...) being random data and completely uninitialized. So sometimes it was non-zero, which allowed the ready() function to fire off. Since the gpu[x].readsearch event was never set in the first place, clGetEventInfo bombed trying to use whatever was at the memory location.
This would be time number 482,847 that accidentally using uninitialized variables has burned me.

Variable declaration performance on loops in Actionscript 3

Despite all known blogs about this issue i always doubt some results and my personal tests shows that the well-said standard isn't the best.
Declaring variables inside the loop, to keep them close to its scope and make it faster to be reached by the method but allocating more memory or declaring outside the for scope to save memory allocation but increase processing to iterate in a distant instance.
My results shows that method B is faster(sometimes), i want to know the background around this.
results vary and im not a bit-brusher guru.
So what you guys think about it?
Method A
var object:Object = new Object();
var loop:int = 100000
for (var i:int = 0; i < loop; i++)
{
object = new Object();
object.foo = foo;
object.bar = bar;
}
OR
Method B
var loop:int = 100000
for (var i:int = 0; i < loop; i++)
{
var object:Object = new Object()
object.foo = foo;
object.bar = bar;
}
AS3 compiler moves all the variable declarations to the top of the method which is called variable hoisting. And the minimum scope for a variable is a complete method. Your method B is equivalent of the following:
var loop:int = 100000;
var i:int;
var object:Object;
for (i = 0; i < loop; i++) {
object = new Object();
object.foo = foo;
object.bar = bar;
}
Note that it only moves the declaration up, not the associated assignment with this. This is the reason you can declare a variable after using it. For example try this code:
trace(a);
var a:int = 10;
trace(a);
This will compile. It's because this code is equivalent of:
var a:int;
trace(a);
a = 10;
trace(a);
This is also the reason that you will get a duplicate variable declaration warning with the following code:
for (var i:int = 0; i < m; i++) {
}
for (var i:int = 0; i < n; i++) { // i is already declared once
}
The concept of variable scope in AS3, JS is different from that of C, C++, Java etc.
tldr; they are semantically equivalent and perform identically.
There is only one variable called object in both cases presented. ActionScript, like JavaScript, "hoists" declarations. That is, var is really just a function-scoped annotation. This differs from C and Java where a new scope (and thus new variable) would have been created in the 2nd case.
There is no difference in AS, however. The engine effectively treats the 2nd code identical to the first. (That being said, I prefer to "keep the var close" to where it is used, while understanding it is not relevant to the scope and has no bearing on performance.)
See Action Script 3.0: Variables and, the Scope section in particular:
The scope of a variable is the area of your code where the variable can be accessed by a lexical reference... In ActionScript 3.0, variables are always assigned the scope of the function or class in which they are declared.
Happy coding.

Resources