How is the timeoutID accessible before initialization? - javascript

function addNew() {
let id = setTimeout( function() {console.log(id)}, 0)
}
addNew()
How is id able to be accessed before initialization? I think this is no different from the second code snippet.
function customTimeout(fn) {
fn()
return 32423
}
function addNew() {
let id = customTimeout( function() {console.log(id)})
}
addNew()

Variables which have not initialized yet cannot be directly referenced by a line that runs until the variable gets initialized (the let id = line), but it's still permitted for functions which have not been called yet to reference the variables inside.
In the first snippet, the setTimeout callback does not run until the let id = line has finished initializing the id variable, so it's permitted.
In the second snippet, the callback passed to customTimeout is running before the let id = line in addNew has finished initializing id, so it's not permitted.
// Permitted:
const fn = () => {
// do something with someVariable
console.log(someVariable);
};
// The above is fine
let someVariable = 'foo';
// just make sure fn is called ONLY AFTER `let someVariable;` runs
fn();
// Not permitted:
const fn = () => {
console.log(someVariable);
};
fn();
let someVariable = 'foo';
// Permitted, since fn is called asynchronously:
function customTimeout(fn) {
Promise.resolve().then(fn);
return 32423
}
function addNew() {
let id = customTimeout( function() {console.log(id)})
}
addNew()
// Permitted, since fn is called asynchronously:
function customTimeout(fn) {
setTimeout(fn);
return 32423
}
function addNew() {
let id = customTimeout( function() {console.log(id)})
}
addNew()
// Permitted, since fn is called after `id` has finished being assigned to:
function customTimeout(fn) {
return [12345, fn];
}
function addNew() {
let [id, fn] = customTimeout( function() {console.log(id)})
fn();
}
addNew()

See example, hope you understand.
function addNew() {
let id = setTimeout( // <-- 1
function() { // <-- 2
console.log(id) // <--- here
}
, 0)
}
addNew()
/*
* It will not work.
*/
function addOld() {
console.log(id)
let id = 2;
}
addOld()

When you call setTimeout (even with a timeout of 0), the callback doesn't run right away. It's placed into a stack to be ran at some point in the future.
So, in your 1st example, you are setting id to the "timeoutID" returned by setTimeout. When the callback is ran in the future (asynchronously), id is there and the callback can see it.
In your 2nd example, you are passing a callback to customTimeout. Before this function returns anything, the callback is ran. It's ran immediately (synchronously) and after the callback is ran, then customTimeout returns a value.
Since this value is returned after the function is ran, the function cannot see it.

Related

How to excute a function only once for same argument

My function call is given below:
await insertingMatchIdsInAllTeamPlayers(fieldersA, matchID)
Suppose the function is called with matchID '1', it should get executed, but if the function is called again with matchId '1'(It will in my case), it should not be executed. However, if it is called with id '2' (basically id !== '1'), it should be executed. I don't care for fieldersA argument.
You could track all passed arguments in array outside of the function. When you call the function, it will check, if the supplied argument is in the array. If it's not, call the function and insert the argument into the array. If the argument is already in the array, don't call the function.
const suppliedMatchIDs = [];
function insertingMatchIdsInAllTeamPlayers(fieldersA, matchID) {
if (suppliedMatchIDs.includes(matchID)) {
return;
} else {
suppliedMatchIDs.push(matchID);
}
// Your function here
}
The general concept of caching arguments to speed up function calls is called memoization.
const matchIdFirstTimeOne = true
if(matchId === 1 && matchIdFirstTimeOne) {
await insertingMatchIdsInAllTeamPlayers(fieldersA, matchID);
matchIFirstTimeOne = false
}
Using closure could solve it.
var insertingMatchIdsInAllTeamPlayers = (function() {
var executed = [];
return function(fieldersA,val) {
if (executed.indexOf(val) == -1) {
executed.push(val);
console.log(val);
}
};
})();
insertingMatchIdsInAllTeamPlayers('',1); // console.log(1)
insertingMatchIdsInAllTeamPlayers('',1); //
insertingMatchIdsInAllTeamPlayers('',2); // console.log(2)
insertingMatchIdsInAllTeamPlayers('',2); //
insertingMatchIdsInAllTeamPlayers('',3); // console.log(3)

JS Module : Difference between directly returning a function in an object and returning a function in an object returning a function

It's redundant but I am learning JS and I want to know how it really works.
Returning a function directly from a module
let func1 = function () {
let test = function () {
console.log("1");
}
return {
getTest : test
}
}
Returning a function by using a function
let func1 = function () {
let test = function () {
console.log("1");
}
return {
getTest : function () {
return test;
}
}
}
In the first case, the getTest property of your object points to a function, so calling it this way:
func1().getTest()
Should result in logging 1.
In the second case, getTest returns a function which returns another function, so you'd have to also call the result in order to get 1, this way:
func1().getTest()();
Calling just getTest will return your function object, rather than calling it.

Invoke and return a function

Is there a way to do this in JS
function namedFunction(elements,args) {
const domElements = document.querySelector(elements);
const initialValue = 0;
let incrementBy = 5;
return function() {
// Do something to domElements based on initialValue and incrementBy
// function needs to run the first time namedFunction is called
// and this is the only function that needs to run on subsequent calls to namedFunction
}.call(null)
// the .call does not work as intended here, but this is basically what I want to do.
}
I think I can do namedFunction()() with the code above in order to invoke both, but I'm wondering if there is another way.
The longer version of the function would look like this:
function namedFunction(elements,args) {
const domElements = document.querySelector(elements);
const initialValue = 0;
let incrementBy = 5;
function namedFunctionEventHandler() {
// Do something to domElements based on initialValue and incrementBy
// function needs to run the first time namedFunction is called
// and this is the only function that needs to run on subsequent calls to namedFunction
}
namedFunctionEventHandler();
return namedFunctionEventHandler;
}
The goal would be to pass a single function as an event handler, that the first time it runs it does initial calculations, caches dom elements and the more heavier stuff, then executes the logic that is abstracted in the returned function and on subsequent calls it uses the data from the closure.
Edit: the namedFunction does not need to accept any arguments, its just for demonstration purposes.
document.addEventListener('scroll', namedFunction)
is what I want to be able to do.
#CertainPerformance - Sorry, I misread your answer.
If you take a look at the end result I would like to achieve, your proposition wont actually work as intended, as if I pass an invoked function as an event handler, its gonna run before an event has actually happened.
You can make namedFunction into an IIFE that saves a reference to a function (initially undefined). On call, if that variable is undefined, carry out the expensive calculations and then assign to the variable; if the variable is defined, then simply call it.
const handler = (() => {
let cheapFn;
return () => {
if (cheapFn) {
cheapFn();
return;
}
// expensive calculations here
const domElements = document.querySelector(elements);
...
cheapFn = () => {
// assign to cheapFn
};
cheapFn();
};
})();
Demo:
const handler = (() => {
let cheapFn;
return () => {
if (cheapFn) {
cheapFn();
return;
}
// expensive calculations here
console.log('expensive');
cheapFn = () => {
console.log('cheap');
};
cheapFn();
};
})();
document.addEventListener('scroll', handler);
body {
height: 400px;
}
body
You can take advantage of the fact that functions in JavaScript are first-class objects, and store the function state (initialized/uninitialized) in a property of the function.
The data computed during initialization can be stored in the function properties as well, please take a look at the demo:
const namedFunction = function(elements,args) {
if (!namedFunction.isInitialized) {
console.log('Initialization: Computing initial value...');
namedFunction.initialValue = 10 * 10;
console.log(`Initial value: ${namedFunction.initialValue}`);
namedFunction.isInitialized = true;
}
return function() {
console.log('Running regular operation:');
console.log(`Current value: ${--namedFunction.initialValue}`);
}.call(null)
}
document.getElementById('demo').addEventListener('click', namedFunction);
<button id="demo">Run</button>

In JavaScript, how to execute next function from an array of functions

I have an array of functions, as in:
funcArray = [func1, func2, func3];
When in a given function, I want to execute the next function in the array. How do I do this? Here is my basic skeleton:
function func1() {
// I get current function caller
var currentFunc = func1.caller;
// I want to execute the next function. Happens to be func2 in the example.
}
I cannot use indexOf function, as one would for an array of strings or numbers.
NOTE: This question appears to be similar to this and the one it refers to. However, it is a different question.
I want to alter the sequence of processing by merely modifying the array. That's the goal. A possibly more efficient approach would be appreciated.
Clarification: Based upon some of the comments:
funcArray is global.
The goal is to implement middleware for a Node.js HTTP module in as simple and efficient a manner as possible without using any third-party modules.
Unless func1 closes over funcArray, you cannot have it reach out and find func2 and execute it, nor should you. Even if func1 does close over funcArray, it would be poor separation of concerns for func1 to reach out and find itself in funcArray and then execute func2.
Instead, have other code that's in charge of running the functions.
If they're synchronous
If the functions complete their work synchronously, then it's simply:
funcArray.forEach(fn => fn());
or
for (const fn of funcArray) {
fn();
}
or if the result of one function should be passed to the next, you can use reduce:
const finalResult = funcArray.reduce((previousResult, fn) => fn(previousResult), undefined);
...where undefined is the value to pass to func1.
If they're asynchronous
If they don't do their work synchronously, you'll need to provide them a way to notify their caller that they've completed their work. Promises are a good, standard way to do that, but you could use simple callbacks instead.
If you make them return promises, for instance, you can use the old promise reduce trick:
funcArray.reduce((p, fn) => {
return p.then(() => {
fn();
});
}, Promise.resolve());
or if the result of one function should be passed to the next:
funcArray.reduce((p, fn) => {
return p.then(fn);
}, Promise.resolve());
You can provide an argument to Promise.resolve to set the value to pass to func1 (without one, it'll receive undefined).
You can bind to the function the index where it is in the array so you can use this index to get and call the next function:
var funcArray = [func1, func2];
var boundFuncArray = funcArray.map((f, i) => f.bind(null, i));
boundFuncArray[0]();
function func1(nextFunctionIndex) {
console.log('func1 called');
// Execute next function:
var nextFunc = boundFuncArray[nextFunctionIndex + 1];
nextFunc && nextFunc();
}
function func2(nextFunctionIndex) {
console.log('func2 called');
// Execute next function:
var nextFunc = boundFuncArray[nextFunctionIndex + 1];
nextFunc && nextFunc();
}
As T.J Crowder stated in the comment below, you can also bind the next function to the current one:
var funcArray = [func1, func2];
var boundFuncArray= funcArray.map((f, i, arr) => f.bind(null, arr[i + 1]));
boundFuncArray[0]();
function func1(nextFunc) {
console.log('func1 called');
// Execute next function:
nextFunc && nextFunc();
}
function func2(nextFunc ) {
console.log('func2 called');
// Execute next function:
nextFunc && nextFunc();
}
You can get the current function's name with arguments.callee.name, loop through the array of functions, and call the next function:
funcArray = [func1, func2, func3];
// Only func1() and func2() will be documented since the others have repeating code
function func1() {
// show the current function name
console.log(arguments.callee.name);
// loop the array of functions
for(var i = 0; i < funcArray.length; ++i)
{
// when the current array item is our current function name and
// another function exists after this then call it and break
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
function func2() {
console.log(arguments.callee.name);
// some logic which switches our next function to be func4()
funcArray[2] = func4;
for(var i = 0; i < funcArray.length; ++i)
{
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
function func3() {
console.log(arguments.callee.name);
for(var i = 0; i < funcArray.length; ++i)
{
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
function func4() {
console.log(arguments.callee.name);
for(var i = 0; i < funcArray.length; ++i)
{
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
// call the first function
funcArray[0]();
Output:
func1
func2
func4
I have solved it this way:
// Adding next options to array
function addNext(array) {
array.last = 1
Object.defineProperty(array, 'next', {get:
function() {
if(this.last < this.length) {
this.last++
return this[this.last-1]
} else {
this.last = 1
return () => {}
}
}
});
}
// The functions for array (has to be function and not arrow function)
function first(param) {
console.log('first',param)
return this.next(param)
}
function second(param) {
console.log('second',param)
return this.next(param)
}
function third(param) {
console.log('third',param)
return this.next(param)
}
// The array
let fns = [first,second,third]
// Adding next option to array
addNext(fns)
// Run first function from array
fns[0]('test')
I dont know if your functions require certain parameters but this is the first thing that came to my mind.
var functArray = [
function() {
console.log("function1 executed");
},
function() {
console.log("function2 executed");
},
function() {
console.log("function3 executed");
},
function() {
console.log("function4 executed");
}];
functArray.forEach(function(x){
x();
});
The accepted answer and other comments did help me, but the way I implemented it is as follows:
//The functions are defined as variables.
//They do not get hoisted, so must be defined first.
func1 = function (arg1, arg2) {
//Code to do whatever...
...
//Execute the next function.
//The name of the function is returned by executing nextFunc()
global[nextFunc()](arg1, arg2, arg3);
}
func2 = function (arg1) { //Note different type of args
...
}
//Note that this is an array of strings representing function names.
funcArray = ["func1", "func2", "func3",...]
//Start the execution...
func1(arg1, arg2);
function nextFunc() {
var currentFuncName = nextFunc.caller.name;
var index = funcArray.indexOf(currentFuncName);
if (index < funcArray.length)
return funcArray[index+1];
}
The sequence of functions to be executed is easily managed through the array funcArray. The number or type of arguments is not fixed for each function. Additionally, the functions control if they should stop the chain or continue with the next function.
It is very simple to understand requiring basic Javascript skills. No overheads of using Promises.
"global" gets replaced by "window" for browser. This is a Node.js implementation. The use of function names in the array will, however, break if you minify the JS code. As I am going to use it on the server, I do not expect to minify it.
You can do it in this way with promise.all if your functions to be executed in parallel.
let toBeExecutedList = [];
toBeExecutedList.push(() => this.addTwoNumber(2, 3));
toBeExecutedList.push(()=>this.square(2));
And Then wherever you want to use them, do it like this:
const resultArr = await Promise.all([
toBeExecutedList.map(func => func()),
]);

Why isn't the variable in this object modified by its callback function?

I'm trying to get a global object to modify one of its own variables in a callback function initialised by one of its own methods. The callback appears to work, but the variable doesn't seem to have been modified when testing the global variable.
Why is the global object not being modified? Are the changes to the global object kept in some sort of staging area pending completion of the callback function?
let obj;
function test_object_flag() {
// 5 - check whether the "timer_finished" flag has been set
console.log("is the timer finished? " + obj.timer_finished); // should return "true"???
}
class TestClass {
constructor() {
this.timer_finished = false;
}
start_timer(ptr_callback_function) {
// 3 - set up a callback for the timer
setTimeout(function() {
// 4 - once the timer is done, set a "timer_finished" flag, and call the callback
this.timer_finished = true;
ptr_callback_function();
}, 1000);
}
}
$( document ).ready(function() {
// 1 - make a new onbject of type TestClass
obj = new TestClass();
// 2 - start the timer
obj.start_timer(test_object_flag);
});
The problem is that setTimeout creates it's own this. Solution may looks like:
start_timer(ptr_callback_function) {
// savig this that your need
const self = this;
setTimeout(function() {
// use needed context
self.timer_finished = true;
ptr_callback_function();
}, 1000);
}
Another option is to use arrow functions:
start_timer(ptr_callback_function) {
setTimeout(() => {
this.timer_finished = true;
ptr_callback_function();
}, 1000);
}

Categories

Resources