1. Print "Hello, world" with a delay of 3 seconds
- setTimeout takes a function as the 1st argument and optional timeout delay & list of values as the function parameters
- setTimeout returns an id (number) which can be used to stop the setTimeout using clearTimeout function
setTimeout(
function (text) {
console.log(text);
},
3000,
"Hello, World"
);
setTimeout(() => console.log("Hello, World"), 3000);
setTimeout(console.log, 3000, "Hello, World");
Notes
Zero or more values that represent any parameters you want to pass to the function when it is run.
References
2. Create a function which receives a function as argument and executes it after 2 seconds
function callbackExec(callback) {
if (typeof callback === "function") {
setTimeout(() => {
callback();
console.log("Callback is executed after 2 seconds");
}, 2000);
}
}
function displayHello() {
console.log("Hello");
}
callbackExec(displayHello);
3. Print numbers from 1 to 10 with delay of 1 second between each value being printed
const num1 = 1,
num2 = 10;
for (let i = num1; i <= num2; i++) {
setTimeout(() => console.log(i), i * 1000);
}
const num1 = 1,
num2 = 10;
(function displayWithDelay(i) {
console.log(i);
if (i !== num2) setTimeout(displayWithDelay, 1000, ++i);
})(1);
Notes
In the 2nd solution, recursive setTimeout is used.
References
4. Print numbers from 1 to 10 with delay of 1 second between each value being printed using setInterval
setInterval
function repeats a block of code at every given timing eventclearInterval
is used to stop the setInterval execution
const num1 = 1,
num2 = 10;
let i = num1;
const intervalId = setInterval(() => {
console.log(i++);
if (i === num2 + 1) clearInterval(intervalId);
}, 1000);
5. Print numbers from 10 to 1 with delay of 1 second between each value being printed using setTimeout using pre ES6 features only
- We can use 3rd parameter of setTimeout to pass the value of iteration which creates a new scope each time loop iterates
- We can also use an inner function scope (IIFE) within the for loop for each iteration
var num1 = 10,
num2 = 1;
for (var i = num1; i >= num2; i--) {
setTimeout(console.log, (num1 - i) * 1000, i);
}
var num1 = 10,
num2 = 1;
for (var i = num1; i >= num2; i--) {
(function (i) {
setTimeout(function () {
console.log(i);
}, (num1 - i) * 1000);
})(i);
}
References
6. Write a utility which prints numbers starting from an initial value and increment in steps which can be started and stopped by the user, any number of times
- The functionality to start and stop can be exposed from a function which internally takes care of incrementing and displaying data
setInterval
can be used to achieve the task and handle the start & stop of data display
function timer(init = 0, step = 1) {
var intervalId;
var count = init;
function startTimer() {
if (!intervalId) {
intervalId = setInterval(() => {
console.log(count);
count += step;
}, 1000);
}
}
function stopTimer() {
clearInterval(intervalId);
intervalId = null;
}
return {
startTimer,
stopTimer,
};
}
// driver code
const timerObj = timer(100, 10);
timerObj.startTimer();
setTimeout(() => {
timerObj.stopTimer();
}, 5000);
Notes
The function can also be modified to have completion after which timer can not be started
7. Execute an array of asynchronous functions one after the other in sequence using callbacks
- The asynchronous function can be simulated using setTimeout which executes the callback
- The array of functions execution can be managed by having a function which takes care of execution of all the async functions
- Asynchronous functions need not be aware of the function to be executed and will take a callback as argument and execute it after completion
function asyncFunc1(callback) {
console.log("Started asyncFunc1");
setTimeout(() => {
console.log("Completed asyncFunc1");
callback();
}, 3000);
}
function asyncFunc2(callback) {
console.log("Started asyncFunc2");
setTimeout(() => {
console.log("Completed asyncFunc2");
callback();
}, 2000);
}
function asyncFunc3(callback) {
console.log("Started asyncFunc3");
setTimeout(() => {
console.log("Completed asyncFunc3");
callback();
}, 1000);
}
function callbackManager(asyncFuncs) {
function nextFuncExecutor() {
const nextAsyncFunc = asyncFuncs.shift();
if (nextAsyncFunc && typeof nextAsyncFunc === "function") {
nextAsyncFunc(nextFuncExecutor);
}
}
nextFuncExecutor();
}
// driver code
callbackManager([asyncFunc1, asyncFunc2, asyncFunc3]);
Notes
3 asynchronous functions are considered here, but the program should work for any number
8. Execute the given list of asynchronous functions in parallel and return the results as an array to the callback
// Example
function asyncFunc1(callback) {
setTimeout(() => {
callback(1);
}, 3000);
}
function asyncFunc2(callback) {
setTimeout(() => {
callback(2);
}, 2000);
}
function asyncFunc3(callback) {
setTimeout(() => {
callback(3);
}, 1000);
}
asyncParallel([asyncFunc1, asyncFunc2, asyncFunc3], (result) => {
console.log(result); // 1, 2, 3 (prints results of each asynchronous function in order)
});
- The async functions can be executed in parallel using the loop and can be tracked for completion with a counter
- The callback function can be sent to the async functions and the results will be stored in the array which will be returned after the completion of all
function asyncParallel(asyncFuncArr, callback) {
const resultArr = new Array(asyncFuncArr.length);
let resultCounter = 0;
asyncFuncArr.forEach((asyncFn, index) => {
asyncFn((value) => {
resultArr[index] = value;
resultCounter++;
if (resultCounter >= asyncFuncArr.length) {
callback(resultArr);
}
});
});
}
References
9. Execute 3 asynchronous functions one after the other in sequence using promise chaining
- The implementation of chaining is that the result is passed through the chain of
then
handlers for all the promises then
method on Promise also returns a promise which can be used to performthen
on the returned promise- The errors in promise / promise chaining can be handled with the error callback for each promise when it settles or with a generic catch block
asyncFunc1().then(
() => {
console.log("Completed async1");
asyncFunc2().then(
() => {
console.log("Completed async2");
asyncFunc3().then(
() => {
console.log("Completed async3");
console.log("All succeeded");
},
(err) => {
console.log("Failure in " + err);
}
);
},
(err) => {
console.log("Failure in " + err);
}
);
},
(err) => {
console.log("Failure in " + err);
}
);
asyncFunc1()
.then(asyncFunc2)
.then(asyncFunc3)
.catch(() => {
console.log("Error occurred in one of the async function");
});
Notes
If then
method has a return statement which is a promise then it will be considered for the next promise chain until it settles
References
10. Execute 3 asynchronous functions one after the other in sequence using async await
- Async function with
await
for each promise can be used to execute in sequence
async function executor(){
try {
await asyncFunc1();
await asyncFunc2();
await asyncFunc3();
console.log('All succeeded');
} catch(){
console.log("Error occured);
}
}();
11. Execute 3 asynchronous functions one after the other in sequence using promise chaining and do not terminate on failure
- The promise which gets rejected will invoke the 2nd function argument to
then
handler - The failure handler will receive the error and continue with next execution which will not propagate failures
async1()
.then(
() => {
console.log("Async1 success");
},
() => {
console.log("Async1 failure");
}
)
.then(async2)
.then(
() => {
console.log("Async2 success");
},
() => {
console.log("Async2 failure");
}
)
.then(async3)
.then(
() => {
console.log("Async3 success");
},
() => {
console.log("Async3 failure");
}
);
12. Execute 3 asynchronous functions one after the other in sequence using async await and do not terminate on failure
- Unlike promises,
try-catch
block can be used on async functions catch
block for each asynchronous function can be used to catch errors and continue with next execution which will not propagate failures
(async function executor() {
try {
await asyncFunc1();
console.log("Async1 success");
} catch {
console.log("Async1 failure");
}
try {
await asyncFunc2();
console.log("Async2 success");
} catch {
console.log("Async2 failure");
}
try {
await asyncFunc3();
console.log("Async3 success");
} catch {
console.log("Async3 failure");
}
console.log("All succeeded");
})();
13. Execute an array of asynchronous functions which returns a promise, one after the other in sequence
- Asynchronous functions can be executed and promises can be captured in an array
- Array method
reduce
can be used to make the sequential execution on promise settlement
const asyncFuncArr = [asyncFunc1, asyncFunc2, asyncFunc3];
asyncFuncArr.reduce((acc, asyncFn) => {
return acc.then(() => asyncFn().then(console.log));
}, Promise.resolve());
const asyncFuncArr = [asyncFunc1, asyncFunc2, asyncFunc3];
asyncFuncArr.reduce(async (acc, asyncFn) => {
await acc;
console.log(await asyncFn());
}, Promise.resolve());
Notes
Promise.resolve()
is used as the initial value to reduce
, which resolves the promise immediately,
14. Execute an array of asynchronous functions simultaneously but print the output in the ordered sequence. Do not wait for printing the data if it already available after promise is settled
- Array method
reduce
can be used to make the simultaneously execution on promise settlement - Unlike sequential execution, the parallel execution of asynchronous functions happen but the output will executed in order of sequence
const asyncFuncArr = [asyncFunc1, asyncFunc2, asyncFunc3];
asyncFuncArr
.map((async) => async())
.reduce((acc, promise) => {
return acc.then(() => promise).then((data) => console.log(data));
}, Promise.resolve());
const asyncFuncArr = [asyncFunc1, asyncFunc2, asyncFunc3];
asyncFuncArr
.map((async) => async())
.reduce(async (acc, promise) => {
console.log(await acc.then(() => promise));
}, Promise.resolve());
15. Design a utility which takes array of asynchronous functions and returns the 1st successful or non successful result with max waiting time set by the user
Promise.race
is an in built JavaScript method which helps us to return the first resolved or rejected promise data from promises array- Timeout feature can be set by adding a function returning a promise which rejects after specified amount of time
- If any promise resolves before timeout the promise which settles first will be the output else timeout will cause rejection
function timeoutFunc() {
const delay = 500;
return new Promise((resolve, reject) => {
setTimeout(() => reject("Timeout"), delay));
}
}
const asyncArr = [asyncFunc1, asyncFunc2, asyncFunc3, timeoutFunc];
const promiseArr = asyncArr.map(asyncFunc => asyncFunc());
Promise.race(promiseArr).then(console.log).catch(console.log);
16. Design a utility which takes URL and a value for attempts which will attempt to make a fetch request. If on failure it tries again with increasing delay for number of times which user has requested
- Utility can designed which returns a promise which attempts to make requests and return the data on success
- The
fetch
request attempts to make calls after increasing time delay on failure - If all the attempts by to get response fails, promise gets rejected
function requestManager(url, attempts = 3) {
return new Promise(async (resolve, reject) => {
for (let i = 0; i < attempts; i++) {
try {
const response = await fetch(url);
resolve(response);
break;
} catch (err) {
if (attempts - 1 === i) {
reject(err);
break;
}
await new Promise((resolve) => setTimeout(resolve, 1000 + 1000 * i));
}
}
});
}
// driver code
requestManager("https://reqbin.com/echo/get/json", 3).then(
(response) => console.log(response),
(error) => console.log(error)
);
Notes
1000 + 1000 * i
is used for delay after 1st unsuccessful attempt to fetch, which increases the delay on every iteration
References
17. Create a generator to return a random number on every request
- The generation of random number can be implemented in the normal way in the function but will returned and function yields
- The function will again continue to execute in loop to return a new random number
function* generatorFunc(param) {
while (true) {
yield Math.ceil(Math.random() * 100);
}
}
// driver code
const it = generatorFunc();
const rand1 = it.next();
const rand2 = it.next();
Notes
Genertor function need not complete its execution
References
18. Search for the presence of a given value in the nested object using generator
- With the help of generator Inversion of control is possible
- Instead of function seaching for the key by passing the callback or key, the logic can be implemented in the controlling code
- For..of loop calling the recursive generator function on object can be used to achieve this
function* objectReader(obj) {
for (let key in obj) {
if (typeof obj[key] === "object") {
yield* objectReader(obj[key]);
} else {
yield obj[key];
}
}
}
// driver code
const it = objectReader({
a: 1,
b: 2,
c: 3,
d: { x: 4, y: 5, z: { m: 6, b: 7 } },
});
const searchValue = 5;
for (let value of it) {
if (value === searchValue) {
console.log(searchValue + " exists");
}
}