Function body not executed if not passed directly to requestAnimationFrame - javascript

So I had this originally:
requestAnimationFrame(appendItemsFragment(itemsFragment, appendItemsFragmentCallback));
But I needed to do something more, so I transformed it to
requestAnimationFrame(() => {
appendItemsFragment(itemsFragment, appendItemsFragmentCallback);
myNewFunctioncall();
});
Original function is defined as:
const appendItemsFragment = (itemsFragment, callback) => () => {
itemsContent.appendChild(itemsFragment);
destroySpinner();
if (callback) {
callback();
}
};
But in the modified version of the requestAnimationFrame, the function appendItemsFragment is not executed (I put an alert() and it does not work, I press F10 two times and it comes back to the end of requestAnimationFrame). Why is that?

appendItemsFrament() returns a callback function. In the original code, requestAnimationFrame() executes that callback. Now you need to do it in your wrapper function.
requestAnimationFrame(() => {
appendItemsFragment(itemsFragment, appendItemsFragmentCallback)();
myNewFunctioncall();
});
However, this will call appendItemsFragment() for every frame that's displayed, which will repeatedly append the item fragment, which is probably not what you want. You need to call it once, save the result, and then call that in your wrapper function.
let callback = appendItemsFragment(itemsFragment, appendItemsFragmentCallback);
requestAnimationFrame(() => {
callback();
myNewFunctioncall();
});

Related

Can setInterval() be put in the last .then() method in Javascript?

A new javascript question, again, and again...
(Updated)
a is a string where I input a word, it will then be sent to a Dictionary API and do the fetchApi(). After getting the result, if a matches the result[0].word, it will trigger the function Func1(), if not then it will trigger the other function Func2().
What I'd like to ask is there any way to use setInterval() for the function data(result), after deleting the part .then(result => data(result)) in the function fetchApi(a)? Or can I only put setInterval() for both functions Func1() and Func2()?
Thank you very much!
var a = "";
function fetchApi(a) {
let url = `${url}${a}`;
fetch(url).then(res => res.json()).then(result => data(result));
}
function data(result) {
// console.log(result);
if (a == result) {
Func1();
} else {
Func2();
}
}
Putting setInterval() on data() will not do anything because result will never change once it has returned. You need to call the whole fetchApi function again if you're expecting the result to be updated later on.

Implement debounce: how to make three invocations result in one effective call?

How can I invoke three times a function with a setTimeOut but just print it once after 100 milliseconds??
This is the definition of debounce that I have to implement:
Debounce ignores the calls made to it during the timer is running and
when the timer expires it calls the function with the last function
call arguments, so I want to achieve that with Javascript
A function will be debounced as follows:
receivedEvent = debounce(receivedEvent, 100)
My attempt:
function debounce(func, timeInterval) {
return (args) => {
setTimeout(func, timeInterval)
}
}
function receivedEvent() {
console.log('receive')
}
receivedEvent();
receivedEvent();
receivedEvent();
But this still generates 3 outputs. I need it to only produce one output according to the requirements.
In your attempt you did not call debounce, but just called your own function receivedEvent. Maybe the site where your attempt is tested will do this for you, but we cannot know this from your question. Just make sure it is called.
To test the requirements you need to use a better use case: one based on a function that receives arguments. This is needed because you must prove that the debounced function is called after the timeout with the last passed arguments.
The key to this pattern is to use variables within a closure:
function debounce(func, timeInterval) {
let timer;
let lastArgs;
return (...args) => {
lastArgs = args; // update so we remember last used args
if (timer) return; // not ready yet to call function...
timer = setTimeout(() => {
func(...lastArgs);
timer = 0; // reset timer (to allow more calls...)
}, timeInterval);
}
}
function receivedEvent(arg) {
console.log('receive ' + arg)
}
receivedEvent = debounce(receivedEvent, 100)
receivedEvent("a");
receivedEvent("b");
receivedEvent("c");
// Output will be "c" after 100ms
Note that the question's definition of "debounce" deviates a bit from its usual definition, where the first invocation actually calls the function immediately, and only then starts the timeout (cooldown-period).

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>

Can you cancel an async function with lodash debounce?

const asyncMethod = async () => {
const data = await fetchData()
return data.map(parseResponse)
})
const a = _.debounce(asyncMethod, 0, { leading: true })
Could you call a.cancel() and also have the async method stop?
Straight from the documentation:
Creates a debounced function that delays invoking func until after
wait milliseconds have elapsed since the last time the debounced
function was invoked. The debounced function comes with a cancel
method to cancel delayed func invocations and a flush method to
immediately invoke them. Provide options to indicate whether func
should be invoked on the leading and/or trailing edge of the wait
timeout. The func is invoked with the last arguments provided to the
debounced function. Subsequent calls to the debounced function return
the result of the last func invocation.
const fn = () => console.log('foo')
const dFn = _.debounce(fn, 500)
dFn()
dFn.cancel() // Remove this to see it working and leave it to cancel
Lets try with setTimeout:
const fn = () => setTimeout(function(){console.log('foo')}, 200)
const dFn = _.debounce(fn, 500)
dFn()
dFn.cancel() // Remove this to see it working and leave it to cancel
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Now if the async function got to the point of being executed there is nothing you can do really.
I had the same problem. My decision is to use AbortController. And invoke both .cancel() and .abort() methods.
See the documentation.

Execute asynchronous javascript code every X seconds

How to execute asynchronous js code (written in node.js) every X seconds and ensure that the new execute/call won't be started before the callback (success, error) from asynchronous function is executed. Code example:
asyncFunc(function(successData){
// do something with successData
}, function(errorData){
// do something with errorData
});
setInterval comes to my mind firstly, but I think it won't ensure what I want, if the callback doesn't execute inside interval, it will go to the queue.
EDIT: To simplify case, repeat async function call 1 second after its callback finishes.
You can use setTimeout like this:
(function loop() {
asyncFunc(function(successData){
setTimeout(loop, X);
}, function(errorData){
// If you still want to continue:
setTimeout(loop, X);
});
})(); // execute immediately
This will start the delay when the async call triggers the callback.
Consider abstracting the problem with a re-usable method that'll allow you to specify the function to call, the interval at which it's called, and the callbacks to invoke:
function repeatAsync(func, interval, success, error) {
(function loop() {
func(
() => {
success();
setTimeout(loop, interval);
},
() => {
error();
setTimeout(loop, interval);
}
);
})(); // invoke immediately
}
If you want the chain of calls to be broken on error, replace the second argument to func with just error, i.e. func( () => { ... }, error)

Categories

Resources