variables scope in nested javascript function calls - javascript

In the below code, I want to know what would be the value of tabId inside doStuffWithReport function. Would it be the same value of tabId that was sent while calling sendMessage or it might change during the course of period?
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
// we suppose that tabId is 5.
function doStuffWithReport(report) {
// what would be the value of tabId? still 5? or it might change to
// something else if, in the meantime, we received another
// call for chrome.tabs.onUpdated?
}
// we are sending a message to tabId 5.
chrome.tabs.sendMessage(tabId, {
text: 'report_me'
}, doStuffWithReport);
});
Edit: I tested it myself. It remains the same, i.e., tabId would remain 5 even if there is another call on chrome.tab.onUpdated.

Well, first let's simplify the code to look at the underlying behavior:
function outside(){
var out = 5;
function inside(){
console.log(out);
}
inside();
}
In this example it will obviously print out 5 when outside is called, but since inside is only defined locally, it will be undefined unless called from within outside.
To make it a bit more complex, let's not make out static and have it based on a parameter:
function outside(out){
function inside(){
console.log(out);
}
inside();
}
Now it is pretty much the same as before and will print out whatever we use as a parameter right away. Let's make it a bit more async by having inside be called after a random amount of time of up to a second and compare the then current value of out with the value of out that is printed at the time inside is called while calling it several times consecutively.
function outside(out){
function inside(){
var dt = Date.now();
console.log(dt + ' : ' +out);
}
var n = Math.floor(Math.random() * 1000);
var d = Date.now()+n;
console.log(d + ' : ' + out);
setTimeout(inside,n);
}
for(var x=0;x<25;x++){outside(x);}
From the output of this, we can see that the value of out that inside has at the time of being called is the same value as when we set the timeout for inside to be called.
From this we can say that tabId would indeed still be 5 when doStuffWithReport is called.

Related

JavaScript - Firebase value to global variable

ref.child("path").on("value", snapshot => {
var z = Object.keys(snapshot.val())[2];
y = snapshot.val()[z];
//Call the friggin thing
exportData(y);
function exportData(y) {
exporting(y);
}
});
function exporting(y) {
var x = y;
//console.log(y);
return x;
}
I want to store x in a global variable.
I cannot do code just below because 'y' will not be passed through.
'y' is a local variable.
var answer = exporting();
answer();
Storing the value in a global variable is not a problem, it's the when that is messing you up.
Data is loaded from Firebase asynchronously. This means that code doesn't run in the order that you probably expected. You can most easily see this by putting some logging in there:
console.log("Before starting to load value");
ref.child("path").on("value", snapshot => {
console.log("Loaded value");
});
console.log("After starting to load value");
When you run this code it prints:
Before starting to load value
After starting to load value
Loaded value
That is probably not the order you expected. But it explains perfectly why the global variable is not set when you access it: the value hasn't come back from the database yet.
That's why you'll want to move the code that needs the data from the database inside the callback function. Alternatively you can use the fact that once() returns a promise, which makes it easier to deal with as a result:
function loadData() {
return ref.child("path").once("value");
});
loadData().then((snapshot) => {
... use the data from snapshot
});
Note that is asynchronous loading is an incredibly common source of confusion for developers who are new to it. I highly recommend checking out some of the other questions on the topic:
How to return value from an asynchronous callback function?
How do I return the response from an asynchronous call?
How to get returned a value by a callback function
Return a value from a Firebase JS callback method (DAL) to another function (controller)
Firebase return output from function
I figured out a solution using jQuery
ref.child("path").on("value", snapshot => {
var y = Object.keys(snapshot.val())[2];
z = snapshot.val()[y];
$("#id").attr("data-stored", z);
updateVal();
});
var x;
function updateVal() {
x = parseFloat($("#id").attr("data-stored"));
}
console.log("It Works! " + x);

Javascript: global var doesn't work. (using SDK.REST.retriveMultipleRecords)

I don't understand why i can't affect my global variable. I don't understand why this doesn't work.. I believe that it has to do with SDK.REST.retriveMultipleRecords, but i dont understand how. As you can see in first alert it works. but in 2nd it shows as "Undefined".
In this fiddle, i have a simple example about defining a global variable and it works.
Can anyone help with this issue? Why it is not affecting my globalVar?
Players = new Array(); //GLOBAL VAR
function setPlayers(topNumber){
SDK.REST.retrieveMultipleRecords(
"gamify_utilizador",
"$select=gamify_utilizadorId,gamify_name,gamify_Ranking&$top="+topNumber+"&$orderby=gamify_Ranking asc",
function (results) {
if(results.length > 0){
for(var i = 0;i<results.length;i++){
Players.push(new Array(results[i].gamify_utilizadorId, results[i].gamify_name));
alert(Players[i]); // ALERT OK
}
}
else {
alert("No Contact records are available to set as the primary contact for the account.");
}
},
errorHandler,
function () {
//OnComplete handler
}
)
alert(Players[1]); // Alert says "Undefined"
};
document.onreadystatechange = function () {
if (document.readyState == "complete") {
setPlayers(4);
alert(Players[1]);
}
}
Instead of alert(Players[1]); try this alert(Players[0]);
Your data is retrieved asynchronously, so it is available at some time after the setPlayers function is executed, and thus some time after the alert(Players[1]) runs.
Here is some code that verifies it:
setTimeout(function(){ alert(Players[1]) }, 3000); // check the variable after 3 seconds
You need to use the data after the success handler (the function which populates the Players array) executes. You could look into the Promises pattern that offers a more traditional interface to asynchronous programming.
think it should be because the inner function has not yet been performed, and thus the array still contains no value at position 1, just after the execution and completion of this array will value this available

How to make a javascript FOR LOOP wait for certain conditions before looping

I have to call up a function (checkImdb) that will fetch some info from a php file (temp.php) and put some contents on a div (placeToFetchTo). This has to be done a certain amount of times, so I used a FOR LOOP for that.
The problem is that only the last instance of the looped counter (currentCastId) gets used. I understand there needs to be a way to force the FOR LOOP to wait for the fetch to be complete, and I have been looking online for answers but nothing seems to work so far. I apologise if I have missed an eventual answer that already exists.
Any help is appreciated.
This is the code I am referring to:
function checkImdb (totalCasts) {
$(function() {
for (currentCastId = 1; currentCastId <= totalCasts; currentCastId++) {
//Gets cast IMDB#
var row = document.getElementById("area2-" + currentCastId)
row = row.innerHTML.toString();
var fetchThis = "temp.php?id=" + row + "\ .filmo-category-section:first b a";
placeToFetchTo = "#area0-" + currentCastId;
function load_complete() {
var filhos = $(placeToFetchTo).children().length, newDiv ="";
var nrMoviesMissing = 0, looped = 0;
alert("done- "+ placeToFetchTo);
}
document.getElementById("area0").innerHTML = document.getElementById("area0").innerHTML + "<div id=\"area0-" + currentCastId + "\"></div>";
$(placeToFetchTo).load(fetchThis, null, load_complete);
} //End of: for (imdbLooper = 0; imdbLooper <= totalCasts; imdbLooper++) {
}); //End of: $(function() {
}
2017 update: The original answer had the callback arg as last arg in the function signature. However, now that the ES6 spread operator is a real thing, best practice is to put it first, not last, so that the spread operator can be used to capture "everything else".
You don't really want to use a for loop if you need to do any "waiting". Instead, use self-terminating recursion:
/**
* This is your async function that "does things" like
* calling a php file on the server through GET/POST and
* then deals with the data it gets back. After it's done,
* it calls the function that was passed as "callback" argument.
*/
function doAsynchronousStuff(callback, ...) {
//... your code goes here ...
// as final step, on the "next clock tick",
// call the "callback" function. This makes
// it a "new" call, giving the JS engine some
// time to slip in other important operations
// in its thread. This basically "unblocks"
// JS execution.
requestAnimationFrame(function() {
callback(/* with whatever args it needs */);
});
}
/**
* This is your "control" function, responsible
* for calling your actual worker function as
* many times as necessary. We give it a number that
* tells it how many times it should run, and a function
* handle that tells it what to call when it has done
* all its iterations.
*/
function runSeveralTimes(fnToCallWhenDone, howManyTimes) {
// if there are 0 times left to run, we don't run
// the operation code, but instead call the "We are done"
// function that was passed as second argument.
if (howManyTimes === 0) {
return fnToCallWhenDone();
}
// If we haven't returned, then howManyTimes is not
// zero. Run the real operational code once, and tell
// to run this control function when its code is done:
doAsynchronousStuff(function doThisWhenDone() {
// the "when done with the real code" function simply
// calls this control function with the "how many times?"
// value decremented by one. If we had to run 5 times,
// the next call will tell it to run 4 times, etc.
runSeveralTimes(fnToCallWhenDone, howManyTimes - 1);
}, ...);
}
In this code the doAsynchronousStuff function is your actual code.
The use of requestAnimationFrame is to ensure the call doesn't flood the callstack. Since the work is technically independent, we can schedule it to be called "on the next tick" instead.
The call chain is a bit like this:
// let's say we need to run 5 times
runSeveralTimes(5);
=> doAsynchronousStuff()
=> runSeveralTimes(5-1 = 4)
=> this is on a new tick, on a new stack, so
this actually happens as if a "new" call:
runSeveralTimes(4)
=> doAsynchronousStuff()
=> runSeveralTimes(4-1 = 3), on new stack
runSeveralTimes(3)
...
=> doAsynchronousStuff()
=> runSeveralTimes(1-1 = 0), on new stack
runSeveralTimes(0)
=> fnToCallWhenDone()
=> return
<end of call chain>
You need to use a while loop and have the loop exit only when all your fetches have completed.
function checkImdb (totalCasts) {
currentCastId = 1;
totalCasts = 3;
doneLoading = false;
while (!doneLoading)
{
//do something
currentCastId++;
if (currentCastId == totalCasts)
doneLoading = true;
}
}

Javascript function call into loop each time

I have function called rotator(id): this function animate div and I can called this function with different id for animate different elements
Actually I use 5 differents id , 1,2,3,4,5
And for call I need put :
rotador(1);rotador(2);rotador(3);rotador(4);rotador(5);
The problem it´s that I want to rotate in automatic mode. For this I think to use this
for (i=0;i<=5;i++) {
setTimeout(rotador(i),2000);
}
But it doesn't work because it animates all in the same time, no let firt execute the first and continue before of first go second , etc , etc and when go the end or number 5 start other time in one
My problem it´s this if you can help me THANKS !!! :) Regards
You are actually calling the rodator(i) function, and schedule for execution after 2 seconds the result of the rodator. In other words, your code is now equalent to:
for (i=0;i<=5;i++) {
var result = rotador(i);
setTimeout(result,2000);
}
You can accomplish this either by creating a function for the callback:
for (i=0;i<=5;i++) {
setTimeout((function(i){
return function(){
rotador(i);
}
})(i),2000 * i);
}
or you can call the next rodator in the rotador function itself:
var rotador = function(i){
// your code
if (i < 5) {
setTimeout(function(){rotaror(i + 1);}, 2000);
}
}
Note: the closure in the second example is needed to call the function with the correct value of i. We are creating an anonymous function, and create i as a local scope variable, which value won't be mutated by the outerscope changes. (we can rename i to n in the local scope, if this would be more readable). Otherwise the value of i will be 5 each time rotador is called, as the value of i would be modified before the actual function call.
since setTimeout() does not wait for the function to be executed before continuing, you have to set the delay to a different value for different items, something like 2000 * (i + 1) instead of just 2000
EDIT: yes, and you need the callback as Darhazer suggests
rotationStep(1);
function rotador(id)
{
console.log(id);
}
function rotationStep( currentId )
{
rotador(currentId);
var nextId = currentId<5 ? currentId+1 : 1;
setTimeout(function(){ rotationStep(nextId) },2000); //anonymous function is a way to pass parameter in IE
}
Use a callback:
setTimeout(function() {
rotador(i)
}, 2000)

Why Nodejs callback() can't access variable outside callback scope?

I am fairly new to NodeJS and to JavaScript in general. Here is my script:
var fs = require('fs') ;
var temp = "???";
var test = function (){
fs.readdir("./", function(err,result){
temp = result; // i change the temp's value
console.log("inter result ....."+temp); // temp's value changed
setTimeout(pr,1000,"inter setTimeout: "+temp); // temp's value changed
});
}
var pr = function (str){
console.log("Print str: "+ str);
} ;
test();
setTimeout(pr,1000,"Out setTimeout print: "+temp); // Why here temp's value not change???
How can I change to the var temp’s value outside the callback?
setTimeout(pr,1000,"Out setTimeout print: "+temp);
is the same as
var str = "Out setTimeout print: " + temp;
setTimeout(pr, 1000, str);
At this point of time temp still is "???". You have to use
setTimeout(function() {
pr("Out setTimeout print: "+temp);
}, 1000);
What order do the log statements appear in your console?
I'm not into node.js, but I would expect to see the "Out" one before the "inter" ones because I would guess the fs.readdir() function is asynchronous and that the callback function that you provide to it will not be executed until after you've already made the call to setTimeout() in the last line of your code at which point temp has not yet been changed.
That is, the sequence of execution I would expect from your code is:
define fs
define temp set to ???
define test function
define pr function
call test() function
within test() call the fs.readdir() but then return immediately from test() without the callback having been executed yet
setTimeout(pr,1000,"Out setTimeout print: "+temp); (where the value of temp at that moment - still "???" - becomes part of the string that setTimeout will pass to pr in one second's time)
the callback from fs.readdir() is executed, and only then does temp get changed. The "inter" timeout gets set.

Categories

Resources