closure example using setTimeout not recognizing function call - javascript

I am trying to create a small application that closes over the delay function and defines a new function using a callback and a wait time. I then wanted to use this newly created inner setTimeout function to take a single parameter that would be run on the callback after the wait time.
function addTwo(num) {
return num + 2
}
const delay = (callback, wait) => {
return setTimeout((value) => callback(value), wait)
}
var delayAddByTwo = delay(addTwo, 100)
console.log(delayAddByTwo(6))
// expected result 8
// actual result --- Uncaught TypeError: delayAddByTwo is not a function
As far as I can tell after the delayAddByTwo = delay(addTwo, 100) the only parameter left to create is the value passed to callback inside the 'inner' setTimeOut function. What am I missing about closure in this example?

You need to replace var delayAddByTwo = delay(addTwo, 100) by var delayAddByTwo = (num) => { delay(() => {addTwo(num)}, 100);}
function addTwo(num) {
console.log(num + 2)
return num + 2
}
const delay = (callback, wait) => {
setTimeout(callback, wait)
}
var delayAddByTwo = (num) => {
delay(() => {addTwo(num)}, 100);
}
console.log(delayAddByTwo)
delayAddByTwo(6)
// expected result 8
// actual result --- Uncaught TypeError: delayAddByTwo is not a function

You need to make delay return another function which accepts n. This n represents the number which will be passed into addTwo (ie the callback). setTimeout by default will return its timeoutID, so, instead, you can return a Promise, which resolves to the result of calling addTwo with n. In order to get your result from the Promise, you can await it within an async function.
See example below:
const addTwo = num => num + 2;
const delay = (callback, wait) => n => new Promise(res => setTimeout(res, wait, callback(n)));
(async () => {
const delayAddByTwo = delay(addTwo, 1000);
const res = await delayAddByTwo(6);
console.log(res);
})();
Above I have used the third argument of setTimeout, which will be used as the first argument of the callback and passed to res.

Change
var delayAddByTwo = delay(addTwo, 100)
To
const delayAddByTwo = delay => (addTwo, 100)

Related

How to implement a different debounce method that the function only called in the nth time

const _debounce = (n, func) => {
//code here
}
const originFun = () => {
console.log('hit')
}
const _call = () => _debounce(2, originFun)
_call() //The originFun not executes
_call() //hit
_call() //The originFun not executes
_call() //hit
I do not know how to implement it, even after a test.
_debounce should accept the sort argument, and the originFun function, and return its own function (or closure) that you can assign to _call. You can then call _call with it's own argument - the number.
function originFun(sort) {
console.log('hit,', sort);
}
function _debounce(sort, func) {
// Set a counter
let count = 1;
// Because closures retain the variables from the
// preceding lexical environment we can
// still use and update `count` when the function is returned
// This function (which accepts a number argument) is what's
// assigned to _call.
return function(n) {
// Call originFun with the number if the remainder of dividing
// sort by count is zero
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Remainder
if (count % sort === 0) func(n);
count++;
}
}
// _debounce returns a function that accepts a number
// argument. We assign that function to _call.
const _call = _debounce(2, originFun);
_call(1) //not execute
_call(2) //hit,2
_call(3) //not execute
_call(4) //hit,4
This implementation of debounce should help your use case. No global variable, and is implemented in pure javascript.
function _debounce(func, n){
let count = 0;
return (...args) => {
count++;
if (count % n === 0) {
count = 0;
func.apply(this, args);
}
};
}
const originFun = (sort) => {
console.log('hit,', sort)
}
const _call = _debounce((sort) => originFun(sort), 2);
_call(1) //not execute
_call(2) //hit,2
_call(3) //not execute
_call(4) //hit,4 */
_call(1) //not execute
_call(2) //hit,2
_call(3) //not execute
_call(4) //hit,4 */
To keep track of the number of calls you could use a global counter.
Additionally, you can pass arguments to your function using the spread syntax. That way you can have multiple arguments and all of them will be passed to the function in order.
let counter = 1;
const _debounce = (n, func, ...args) => {
if (counter === n) {
counter = 1;
func(...args);
} else {
counter += 1;
}
}
const originFun = (sort) => {
console.log('hit,', sort)
}
const _call = (sort) => _debounce(2, originFun, sort)
_call(1) //not execute
_call(2) //hit,2
_call(3) //not execute
_call(4) //hit,4

Javascript - Promises

Hello I need help on my code. I am not really familiar with Promises. I created a function called EmitRandomNumber(). In this function, after 2 full seconds (2000 ms), it generates a random number between 0 to 100. If the random number generated is below 80, I need to call that function again, up to 10 times, until the random number generated is greater than 80.
let attempt = 1;
let rN;
function EmitRandomNumber() {
return new Promise((resolve, reject)=> {
console.log(`Attempt #${attempt}. EmitRandomNumber is called.`);
setTimeout(()=>{
let randomNumber = Math.floor(Math.random() * 100) + 1;
rN = randomNumber;
console.log("2 seconds have passed.");
if(randomNumber>=80&&attempt<=10){
console.log(randomNumber,attempt);
resolve();
}else if(randomNumber<80&&attempt<=10){
attempt++;
console.log(`Random number generated is ${randomNumber}.`);
console.log("===============================");
EmitRandomNumber();
}
},2000);
});
}
let promise = EmitRandomNumber();
promise.then(()=>{
console.log(`Random number generated is ${rN}!!!!`);
console.log("===============================");
}).catch(()=>{
console.log("End");
});
I dont know if I am using the promise properly and sometimes when it is above 80 it doesnt execute whatever code is in the resolve. Can you help me how I can fix my code. Thank you!
So the code is mostly good, you just did not specify when to 'reject' the promise. From what you described you want this to 'reject' (or display console.log('End')) IF the promise does not find a number after 10 attempts. So you only needed to add a condition (if attempt===10) etc.
EDIT: The resolve and reject need to be returned.
let attempt = 1;
let rN;
function EmitRandomNumber() {
return new Promise((resolve, reject)=> {
console.log(`Attempt #${attempt}. EmitRandomNumber is called.`);
setTimeout(()=>{
let randomNumber = Math.floor(Math.random() * 100) + 1;
rN = randomNumber;
console.log("2 seconds have passed.");
if(randomNumber>=80&&attempt<=10){
console.log(randomNumber,attempt);
return resolve();
}else if(randomNumber<80&&attempt<=10){
attempt++;
console.log(`Random number generated is ${randomNumber}.`);
console.log("===============================");
EmitRandomNumber();
//CHECK FOR CONDITION NUMBER OF ATTEMPTS
} else if(attempt>10){
return reject();
}
},2000);
});
}
let promise = EmitRandomNumber();
promise.then(()=>{
console.log(`Random number generated is ${rN}!!!!`);
console.log("===============================");
}).catch(()=>{
console.log("End");
});
It may be easier to read and reason about using async functions and await. E.g.:
const MAX_TRIES = 10;
const sleep = (ms) => {
return new Promise(resolve => setTimeout(resolve, ms));
}
const getRandomNumber = () => {
return Math.floor(Math.random() * 100) + 1;
}
const getNumberWaitAndReturn = async () => {
let rN = getRandomNumber();
let attempt = 1;
while (rN < 80 && attempt <= MAX_TRIES) {
await sleep(2000);
rN = getRandomNumber();
attempt += 1;
}
if (attempt > MAX_TRIES) {
console.log(attempt)
throw new Error("No luck! It took more than 10 tries!")
}
return {
rN,
attempt
}
}
const result = getNumberWaitAndReturn().then(v => {
console.log(v)
});
You doing pretty good, you just need a little more practice to do it better, I highly recommend you start using closure instead of declaring global variables as you are doing into your code.
Before tell you how you can solve this, I will tell you that are the error, your function will only work as you expect if the first time its executed it generates a random number above of 80, otherwise the function will never be resolved, why ? that's because everytime you are calling the EmitRandomNumber() into the function itself you are creating a new Promise reference so the first one that you created is never handled because you create a new reference promise, so the new reference, it's no handle because your are calling inside the function itself and nobody is doing .then(...).
So how to solve this ? First at all you need to create a closure to create this.
const emitRandomNumber = () => {
const MAX_ATTEMPTS = 10;
const MS_START_TIME = 2000;
const MINIMUM_NUMBER = 80;
const MAXIMUM_NUMBER = 100;
let attempts = 0;
const randomNumber = (max) => Math.floor(Math.random() * max);
return new Promise((resolve, reject) => {
const startRandomNumber = () => {
attempts++;
const number = randomNumber(MAXIMUM_NUMBER);
if (number < MINIMUM_NUMBER && attempts <= MAX_ATTEMPTS) {
return setTimeout(startRandomNumber, MS_START_TIME);
}
if (number < MINIMUM_NUMBER && attempts > MAX_ATTEMPTS) {
return reject();
}
resolve(number);
};
setTimeout(startRandomNumber, MS_START_TIME);
});
}
As you can see we are using closure to create function inside function body, so the only function that we call again it's the startRandomNumber function, so this won't generate new promise instances, instead we are keeping the same promise reference and we just execute the random number function until we reach the attempts.

How to measure the execution time of an asynchronous function in nodejs?

I'm trying to get the execution/response time of an asynchronous function that executes inside a node-fetch operation, like the following
async function getEditedData() {
var a = await fetch(`https://api.example.com/resorce_example`);
var b = await a.json();
// Some aditional treatment of the object obtained (b)
console.log("End of the asynchronous function")
}
I Used the library perf_hooks like this, but the execution time shows before
const hrtime = require ('perf_hooks').performance.now ;
var start = hrtime ();
getEditedData();
var end = hrtime ();
console.log (end - start);
I found the async_hooks library https://nodejs.org/api/perf_hooks.html#perf_hooks_measuring_the_duration_of_async_operations , but I canĀ“t understand how it works. I am a basic in javascript/nodejs
You could simply store Date.now() in some variable and then check Date.now() when your Promise resolves (or rejects) and subtract to find the difference. For example:
const simulateSomeAsyncFunction = new Promise((resolve, reject) => {
console.log('Initiating some async process, please wait...')
const startTime = Date.now();
setTimeout(() => {
resolve(Date.now() - startTime);
}, 3000);
});
simulateSomeAsyncFunction.then(msElapsed => {
console.log(`Async function took ${msElapsed / 1000} seconds to complete.`);
});
Note: You could write code that achieves the same thing and appears to be synchronous by using await/async since that is just "syntactic sugar" built on top of Promises. For example:
const simulateSomeAsyncFunction = () => {
console.log('Initiating some async process, please wait...');
return new Promise((resolve, reject) => {
setTimeout(resolve, 3000);
});
};
// Await is required to be called within an async function so we have to wrap the calling code in an async IIFE
(async() => {
const startTime = Date.now();
await simulateSomeAsyncFunction();
const msElapsed = Date.now() - startTime;
console.log(`Async function took ${msElapsed / 1000} seconds to complete.`);
})();
Starting with a simple async function -
const fakeAsync = async (value) => {
const delay = 2000 + Math.random() * 3000 // 2 - 5 seconds
return new Promise(r => setTimeout(r, delay, value))
}
fakeAsync("foo").then(console.log)
console.log("please wait...")
// "please wait..."
// "foo"
We could write a generic function, timeit. This is a higher-order function that accepts a function as input and returns a new function as output. The new function operates like a decorated version of the original -
const timeit = (func = identity) => async (...args) => {
const t = Date.now()
const result = await func(...args)
return { duration: Date.now() - t, result }
}
// decorate the original
const timedFakeAsync = timeit(fakeAsync)
// call the decorated function
timedFakeAsync("hello").then(console.log)
timedFakeAsync("earth").then(console.log)
// { duration: 3614, result: "earth" }
// { duration: 4757, result: "hello" }
The timed version of our function returns an object, { duration, result }, that reports the runtime of our async function and the result.
Expand the snippet below to verify the results in your own browser -
const identity = x =>
x
const timeit = (func = identity) => async (...args) => {
const t = Date.now()
const result = await func(...args)
return { duration: Date.now() - t, result }
}
const fakeAsync = async (value) => {
const delay = 2000 + Math.random() * 3000 // 2 - 5 seconds
return new Promise(r => setTimeout(r, delay, value))
}
const timedFakeAsync = timeit(fakeAsync)
timedFakeAsync("hello").then(console.log)
timedFakeAsync("earth").then(console.log)
console.log("please wait...")
// "please wait..."
// { duration: 3614, result: "earth" }
// { duration: 4757, result: "hello" }
If you expect to set the end after getEditedData() is completed, you actually need to await getEditedData(). Otherwise, you'll go right past it while it executes... asynchrnously.

Can a javascript async method return multiple values at different times?

I have a list of values that I wanna return asynchronously, but a limited number of values at a time.
So I call my function once and it has an array of say 20 distinct elements say [1,2,...,20], and it keeps on returning 5 elements every second. So I want:
[1,2,..,5] at 0 sec,
[6,7,..,10] at 1 sec,
and so on....
I was thinking on using buffer files. If it is a valid solution, can someone tell me how to implement it for the given example?
An async generator function (or method) can yield (loosely, return) a sequence of values over time. Here's an example:
function sleepRandom(maxMs) {
return new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * maxMs)));
}
async function *example(max) {
yield -1; // Example of explicit value
// Wait 500ms
await sleepRandom(800);
// Example of loop
for (let i = 0; i < max; ++i) {
// Wait 500ms
await sleepRandom(800);
// Yield current loop value
yield i;
}
// All done
yield "Done";
}
(async () => {
for await (let value of example(10)) {
console.log(value);
}
})()
.catch(error => {
console.error(error);
});
The * after function is what makes it a generator function. The async before function is what makes it async.
You could use nodes EventEmitter for that:
var events = require('events');
function spitChunks(arr, chunksize = 5, delay = 1000, em = null) {
if(!em) em = new events.EventEmitter();
if(arr.length > 0) setTimeout(() => {
em.emit('nextChunk', arr.splice(0,chunksize));
spitChunks(arr, chunksize, delay, em);
}, delay);
return em;
}
let numbers = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
let emitter = spitChunks(numbers);
emitter.on('nextChunk', data => console.log(data));

Add delay to recursive function without modifying it

Let's say function fib():
function fib(n) {
if (n < 2){
return n
}
return fib(n - 1) + fib (n - 2)
}
Now, let's say I want to display each step of this recursive function in a document.write, and progressively add the result of each iteration with a delay of 1000ms between steps. Can I do it without modifying the original function by perhaps having another function, passing this one as the argument, creating the output mechanism, and since it also returns a function, recursively add the delay?
No, but writing it as a generator instead would give you a useful interface to implement something like that
function*fib() {
for (let a = 1, b = 1, c = 0;; c = a+b, a = b, b = c) yield a;
}
const sleep = ms => new Promise(resolve => setTimeout(() => resolve(), ms));
const gen = fib();
// then, use it step by step
console.log(gen.next().value);
console.log(gen.next().value);
// OR with a delay inbetween
async function slowly() {
for (let v of gen) {
console.log(v);
await sleep(1000);
}
}
slowly();
Because your original function is synchronous, without modifying you cannot really call it as if it were asynchronous.
JavaScript allows you to overwrite a symbol like your function, fib. This allows you to redefine it whatever you just want. Maybe you could make it asynchronous with dynamically added behavior, I don't know, but that would be too complicated.
However, you said "I want to display each step of this recursive function ... with a delay of 1000 ms between steps". You can easily do this, because you can call fib synchronously, but print the results asynchronously! Example:
function fib(n) {
if (n < 2){
return n
}
return fib(n - 1) + fib (n - 2)
}
var queue = [];
var depth = 0;
var manageCall = function(fn){
return function() {
++depth;
let result = fn.apply(this, arguments);
--depth;
queue.push(" ".repeat(depth)+fn.name+"("+arguments[0]+") = "+result);
return result;
};
};
var fib = manageCall(fib);
fib(8);
var printDelayed = function() {
if (queue.length != 0) {
console.info(queue.pop());
setTimeout(printDelayed, 1000);
}
}
printDelayed();
fib is unchanged, but can follow how the recursion were executed.
Yeah, so ... You probably actually can do this, but you're gonna have to get really creative. This is extremely non-performant code, and likely would need some tweaks to actually function, but you could conceivably take this just a bit further to get what you're after.
What we're doing
So, we're going to be ripping out the guts of a defined function that's passed to our mangler function waitAndPrintFunc. That function will output the function as a string, and then use it to rebuild a Frankenstein function that's executed via eval.
PLEASE NOTE: Don't EVER use this in a production environment. This code is a living abomination just to prove that something like this could be done.
//global
let indexCounter = 0;
const waitAndPrintFunc = (func) => {
let wholeFunc = func.toString();
const funcName = wholeFunc.slice(8, wholeFunc.indexOf('(')).replace(' ', '');
let funcBody = wholeFunc.slice(wholeFunc.indexOf('{') + 1, wholeFunc.lastIndexOf('}'));
const returnIndex = funcBody.indexOf(`return ${funcName}`);
const meatyPart = funcBody.slice(returnIndex + 7);
wholeFunc = wholeFunc.split('');
funcBody = funcBody.split('');
funcBody.splice(
returnIndex,
funcBody.length - returnIndex,
`document.querySelector('.output').appendChild("step \${indexCounter++}: \${eval(meatyPart)}"); setTimeout(() => {${meatyPart}}, 1000);`
);
wholeFunc.splice(0, 9 + funcName.length, 'const MyRiggedFunction = ');
wholeFunc.splice(wholeFunc.indexOf(')') + 1, 0, ' => ');
wholeFunc.splice(wholeFunc.indexOf('{') + 1, wholeFunc.lastIndexOf('}'), ...funcBody);
console.log(wholeFunc.join(''))
eval(`${wholeFunc.join('')} ; MyRiggedFunction(1)`);
};
function fib(n) {
if (n < 2) {
return n;
}
return fib(n - 1) + fib(n - 2);
}
waitAndPrintFunc(fib);
I see that I can intercept a function call, if I intercept fib(n), it should be intercepted by its own recursive calls, right? https://bytes.babbel.com/en/articles/2014-09-09-javascript-function-call-interception.html I will try it
No, you can't do that.
You could definitely "tap" into fib in order to add some console.log:
// original function
function fib(n) {
if (n < 2){
return n
}
return fib(n - 1) + fib (n - 2)
}
// rebind the var `fib` to a wrapper to the original `fib`
var fib = ((orig) => (n) => {
console.log('tap:', n);
return orig(n)
}
)(fib);
console.log("result:", fib(7));
However if the prerequisite is that you cannot modify the original fib that means it's still working as synchronous function: adding the delay means that fib becomes asynchronous.
But the return value of fib itself, since it's recursive, is using an addition operator (fib(n - 1) + fib(n - 2)), and therefore is expecting an immediate value, not something delayed.
If the constraint is that you cannot modify the original fib but you can only tap into it, you can't add a timeout given the code you provided.
Said that, you can definitely tap into the function and schedule a console.log every 1000ms: however that means that the function is finished already to be executed, it's just the console.log for each step that is delayed.
And I don't think that's what you want.

Categories