Round 2 Answer Sheet | Prepare well for JavaScript Interview

Topic Covered:

1. Implement a function that serializes a Javascript value into a JSON string.
2. Implement a function that performs a deep copy of a value, but also handles circular references.
3. Implement a function that determines if two values are deep equal.
4. Implement the functionality behaviour of Promise.any
5. Implement the functionality behaviour of Promise.allSettled
6. Implement a function that returns a memoized version of a function which accepts a single argument.
7. Implement a function that deserializes a JSON string into a Javascript value.
8. Implement a class that can subscribe to and emit events that trigger attached callback functions.
9. Implement a debounce function that comes with a cancel method to cancel delayed invocations.
10. Implement a function that recursively flattens an array into a single level deep.
11. Implement a promisify function that allows the original function to override the return value.
12. Implement a function to execute N async tasks in series.
13. Implement a function to execute N async tasks in parallel.
14. Implement a function to execute N async tasks in race.
15. Implement a pipe function which chains N number of functions.
16. Implement negative indexing in Array using Proxies.
17. Implement Lodash_.get method which gets value from the path.
18. Implement your custom version of the call method which sets the “this” context.
19. Implement throttling of promises which throttles API requests to max limit.
20. Implement memoizing or caching identical API requests.
21. Implement a curried function with placeholders support.
22. Implement a custom polyfill version of Object.assign
23. Implement custom Virtual DOM I which serializes the data in valid javascript objects.
24. Implement custom Virtual DOM II which deserializes the data.
25. Implement a custom polyfill function memoize from the Lodash library.
26. Imlpement a custom String Tokenizer.
27. Implement a custom function _chunk() which chunks the arrays from Lodash Library.
28. Implement the polyfills for the call, apply, bind methods from scratch.
29. Implement a throttle function that comes with a cancel method to cancel delayed invocations.
30. Write a custom polyfill for typeof operator which returns the correct typeof for the Javascript value.

https://sonikamaheshwari067.medium.com/prepare-well-for-javascript-interview-questions-eb51f458db12

1. Implement a function that serializes a Javascript value into a JSON string.

Check out this Article:

https://levelup.gitconnected.com/javascript-google-interview-question-polyfill-of-stringify-and-parse-c9b370b11027?source=your_stories_page-------------------------------------


function stringify(value) {
// If the value is null, return the string "null"
if (value === null) {
return "null";
}

// If the value is a number or a boolean, convert it to a string and return
if (typeof value === 'number' || typeof value === 'boolean') {
return value.toString();
}

// If the value is a string, return it enclosed in double quotes
if (typeof value === 'string') {
return `"${value}"`;
}

// If the value is an array, recursively call stringify on each element,
// join the resulting strings with commas, and enclose in square brackets
if (Array.isArray(value)) {
const arrayContents = value.map((element) => stringify(element)).join(',');
return `[${arrayContents}]`;
}

// If the value is a plain object (not a function, etc.), recursively call
// stringify on each value, ignore non-serializable values (like undefined and functions),
// and construct a string with each key-value pair enclosed in curly braces
if (typeof value === 'object') {
const keys = Object.keys(value);
const keyValuePairStrings = keys.map((key) => {
const valString = stringify(value[key]);
if (valString === undefined || typeof value[key] === 'function') {
// Skip undefined and functions since they are not valid JSON
return '';
}
return `"${key}":${valString}`;
}).filter(Boolean); // Remove any undefined values resulting from non-serializable values
return `{${keyValuePairStrings.join(',')}}`;
}

// For all other types that are not serializable to JSON, such as undefined or functions,
// return undefined (which will be filtered out in the object case)
return undefined;
}


console.log(stringify(myObject));

OR


if (!window.JSON) {
window.JSON = {
stringify: function(obj) {
var type = typeof(obj);

// Handle primitive types
if (type !== 'object' || obj === null) {
if (type === 'string') {
obj = '"' + obj + '"';
}
return String(obj);
}

// Handle arrays
else if (Array.isArray(obj)) {
var arrayStr = '[';
for (var i = 0; i < obj.length; i++) {
arrayStr += (i ? ',' : '') + this.stringify(obj[i]);
}
return arrayStr + ']';
}

// Handle objects
else {
var properties = [];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
var val = this.stringify(obj[prop]);
if (val !== undefined) {
properties.push('"' + prop + '":' + val);
}
}
}
return '{' + properties.join(',') + '}';
}
}
};
}

var myObject = {
name: "Alice",
age: 30,
isStudent: false,
hobbies: ["reading", "gaming", "hiking"],
details: {
hair: "brown",
height: 165
},
// Non-serializable properties (functions) will be ignored in the output
greet: function() { return `Hello, my name is ${this.name}`; }
};

2. Implement a function that performs a deep copy of a value, but also handles circular references.

Check out this article:

function deepCopyWithCircularDetection(obj, copiedObjects = []) {
// If the value is primitive or null, return it directly
if (typeof obj !== 'object' || obj === null) {
return obj;
}

// Check if this object has been copied before
const foundIndex = copiedObjects.findIndex(item => item.original === obj);
if (foundIndex !== -1) {
// Return the copied version of the circular reference
return copiedObjects[foundIndex].copy;
}

// Create a new object or array to store the copied properties
let copy = Array.isArray(obj) ? [] : {};

// Store the original object and its copy
copiedObjects.push({ original: obj, copy });

// Copy all properties recursively
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepCopyWithCircularDetection(obj[key], copiedObjects);
}
}

return copy;
}

// Example usage:
const obj = {
name: "John",
age: 30,
address: {
city: "New York",
postalCode: "10001"
}
};
obj.self = obj; // Circular reference

const copiedObj = deepCopyWithCircularDetection(obj);
console.log(copiedObj);

3. Implement a function that determines if two values are deep equal.

Checkout this article: https://sonikamaheshwari067.medium.com/compare-2-nested-objects-in-javascript-imp-42fa6a1fea14


function deepEqual(value1, value2) {
// Check if the values are strictly equal
if (value1 === value2) {
return true;
}

// Check if both values are objects
if (typeof value1 === 'object' && typeof value2 === 'object' && value1 !== null && value2 !== null) {
// Check if one is an array and the other is not
if (Array.isArray(value1) !== Array.isArray(value2)) {
return false;
}

// If both are arrays, compare each element recursively
if (Array.isArray(value1)) {
if (value1.length !== value2.length) {
return false;
}
for (let i = 0; i < value1.length; i++) {
if (!deepEqual(value1[i], value2[i])) {
return false;
}
}
return true;
}

// Get the keys of both objects
const keys1 = Object.keys(value1);
const keys2 = Object.keys(value2);

// If the number of keys is different, they are not deep equal
if (keys1.length !== keys2.length) {
return false;
}

// Check each key-value pair recursively
for (let key of keys1) {
if (!keys2.includes(key) || !deepEqual(value1[key], value2[key])) {
return false;
}
}

return true;
}

// If the values are not strictly equal and not objects, they are not deep equal
return false;
}

// Example usage:
const obj1 = {
name: "John",
age: 30,
address: {
city: "New York",
postalCode: "10001"
},
hobbies: ["reading", "swimming"]
};

const obj2 = {
name: "John",
age: 30,
address: {
city: "New York",
postalCode: "10001"
},
hobbies: ["reading", "swimming"]
};

console.log(deepEqual(obj1, obj2)); // Output: true

5. Implement the functionality behaviour of Promise.allSettled

The Promise.allSettled() function returns a promise that resolves after all of the provided promises have either resolved or rejected, with an array of objects that each describe the outcome of each promise.

Each object has two properties:

  • status: Indicates the status of the promise, which can be either "fulfilled" or "rejected".
  • value or reason: Contains the value (if fulfilled) or the reason (if rejected) for the promise.

Checkout this article:


/* [{
status: "fulfilled",
value: "apple"
}, {
status: "fulfilled",
value: "banana"
}, {
reason: "orange",
status: "rejected"
}] */


const allSettled = (promises) => {
if (!promises.length) {
return Promise.resolve([]);
}

const poolResponses = [];
let counter = 0;

return new Promise((resolve) => {
promises.forEach((promise, i) => {
promise
.then((res) => {
poolResponses[i] = {
status: 'fulfilled',
value: res,
};
})
.catch((err) => {
poolResponses[i] = {
status: 'rejected',
reason: err,
};
})
.finally(() => {
counter++;
if (counter === promises.length) {
resolve(poolResponses);
}
});
});
});
};

OR


function promiseAllSettled(promises) {
return Promise.all(promises.map(promise => {
return promise
.then(value => {
return { status: 'fulfilled', value };
})
.catch(reason => {
return { status: 'rejected', reason };
});
}));
}

// Example usage:
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'Rejected'));
const promise3 = new Promise((resolve, reject) => setTimeout(resolve, 200, 'Resolved'));

promiseAllSettled([promise1, promise2, promise3])
.then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log(`Fulfilled: ${result.value}`);
} else {
console.error(`Rejected: ${result.reason}`);
}
});
});

6. Implement a function that returns a memoized version of a function which accepts a single argument.


function memoize(func) {
const cache = new Map();

return function(arg) {
if (cache.has(arg)) {
return cache.get(arg);
} else {
const result = func(arg);
cache.set(arg, result);
return result;
}
};
}

// Example usage:
function square(x) {
console.log(`Calculating square of ${x}`);
return x * x;
}

const memoizedSquare = memoize(square);

console.log(memoizedSquare(5)); // Output: Calculating square of 5, 25
console.log(memoizedSquare(5)); // Output: 25 (No calculation)
console.log(memoizedSquare(6)); // Output: Calculating square of 6, 36
console.log(memoizedSquare(6)); // Output: 36 (No calculation)

OR


function memoize(func) {
const cache = new Map();

return function(arg) {
if (cache.has(arg)) {
return cache.get(arg);
} else {
const result = func(arg);
cache.set(arg, result);
return result;
}
};
}

function factorial(n) {
if (n === 0 || n === 1) {
return 1;
}
return n * factorial(n - 1);
}

const memoizedFactorial = memoize(factorial);

console.log(memoizedFactorial(5)); // Output: 120
console.log(memoizedFactorial(5)); // Output: 120 (No calculation, result is cached)
console.log(memoizedFactorial(6)); // Output: 720
console.log(memoizedFactorial(6)); // Output: 720 (No calculation, result is cached)

7. Implement a function that deserializes a JSON string into a Javascript value.

Checkout this article: https://levelup.gitconnected.com/javascript-google-interview-question-polyfill-of-stringify-and-parse-c9b370b11027


function deserializeFromJson(jsonString) {
try {
return JSON.parse(jsonString);
} catch (error) {
console.error("Error deserializing from JSON:", error);
return null;
}
}

// Example usage:
const jsonString = '{"name":"John","age":30,"city":"New York"}';
const deserializedValue = deserializeFromJson(jsonString);
console.log(deserializedValue); // Output: { name: 'John', age: 30, city: 'New York' }

Implement,


if (!window.JSON) {
window.JSON = {};
}

window.JSON.parse = function(jsonString) {
// Check if the argument is a string
if (typeof jsonString !== 'string') {
throw new TypeError('JSON.parse: First argument must be a string');
}

// Remove leading and trailing whitespace
jsonString = jsonString.trim();

// Check if the string is empty
if (jsonString === '') {
throw new SyntaxError('JSON.parse: Unexpected end of data');
}

// Check if the string starts and ends with curly braces or square brackets
if ((jsonString[0] !== '{' && jsonString[0] !== '[') || (jsonString[jsonString.length - 1] !== '}' && jsonString[jsonString.length - 1] !== ']')) {
throw new SyntaxError('JSON.parse: Unexpected token');
}

// Initialize the position and depth variables for parsing
let position = 0;
let depth = 0;

// Skip whitespace characters
const skipWhitespace = () => {
while (position < jsonString.length && /\s/.test(jsonString[position])) {
position++;
}
};

// Parse a value according to its type
const parseValue = () => {
const currentChar = jsonString[position];
if (currentChar === '{') {
return parseObject();
} else if (currentChar === '[') {
return parseArray();
} else if (currentChar === '"') {
return parseString();
} else if (currentChar === '-' || /\d/.test(currentChar)) {
return parseNumber();
} else if (jsonString.slice(position, position + 4) === 'true') {
position += 4;
return true;
} else if (jsonString.slice(position, position + 5) === 'false') {
position += 5;
return false;
} else if (jsonString.slice(position, position + 4) === 'null') {
position += 4;
return null;
} else {
throw new SyntaxError('JSON.parse: Unexpected token');
}
};

// Parse an object
const parseObject = () => {
const obj = {};
position++; // Move past the opening curly brace
depth++;

skipWhitespace();
if (jsonString[position] === '}') {
position++; // Move past the closing curly brace
depth--;
return obj; // Empty object
}

while (true) {
skipWhitespace();
if (jsonString[position] !== '"') {
throw new SyntaxError('JSON.parse: Expected string key');
}
const key = parseString();
skipWhitespace();
if (jsonString[position] !== ':') {
throw new SyntaxError('JSON.parse: Expected colon');
}
position++; // Move past the colon
skipWhitespace();
const value = parseValue();
obj[key] = value;
skipWhitespace();

if (jsonString[position] === ',') {
position++; // Move past the comma
} else if (jsonString[position] === '}') {
position++; // Move past the closing curly brace
depth--;
break;
} else {
throw new SyntaxError('JSON.parse: Unexpected token');
}
}

return obj;
};

// Parse an array
const parseArray = () => {
const arr = [];
position++; // Move past the opening square bracket
depth++;

skipWhitespace();
if (jsonString[position] === ']') {
position++; // Move past the closing square bracket
depth--;
return arr; // Empty array
}

while (true) {
skipWhitespace();
const value = parseValue();
arr.push(value);
skipWhitespace();

if (jsonString[position] === ',') {
position++; // Move past the comma
} else if (jsonString[position] === ']') {
position++; // Move past the closing square bracket
depth--;
break;
} else {
throw new SyntaxError('JSON.parse: Unexpected token');
}
}

return arr;
};

// Parse a string
const parseString = () => {
let result = '';
position++; // Move past the opening double quote

while (position < jsonString.length) {
const currentChar = jsonString[position];
if (currentChar === '\\') {
position++;
const escapeChar = jsonString[position];
if (escapeChar === 'b') {
result += '\b';
} else if (escapeChar === 'f') {
result += '\f';
} else if (escapeChar === 'n') {
result += '\n';
} else if (escapeChar === 'r') {
result += '\r';
} else if (escapeChar === 't') {
result += '\t';
} else if (escapeChar === '"' || escapeChar === '\\' || escapeChar === '/') {
result += escapeChar;
} else if (escapeChar === 'u') {
const hex = jsonString.slice(position + 1, position + 5);
if (!/^[0-9A-Fa-f]{4}$/.test(hex)) {
throw new SyntaxError('JSON.parse: Invalid Unicode escape sequence');
}
result += String.fromCharCode(parseInt(hex, 16));
position += 4;
} else {
throw new SyntaxError('JSON.parse: Invalid escape character');
}
} else if (currentChar === '"') {
position++; // Move past the closing double quote
return result;
} else {
result += currentChar;
}
position++;
}

throw new SyntaxError('JSON.parse: Unterminated string literal');
};

// Parse a number
const parseNumber = () => {
let numStr = '';
let currentChar = jsonString[position];

if (currentChar === '-') {
numStr += currentChar;
position++; // Move past the minus sign
currentChar = jsonString[position];
}

if (currentChar === '0') {
numStr += currentChar;
position++; // Move past the digit
} else if (/\d/.test(currentChar)) {
while (/\d/.test(currentChar)) {
numStr += currentChar;
position++;
currentChar = jsonString[position];
}
} else {
throw new SyntaxError('JSON.parse: Unexpected token');
}

if (currentChar === '.') {
numStr += currentChar;
position++; // Move past the decimal point
currentChar = jsonString[position];
if (!/\d/.test(currentChar)) {
throw new SyntaxError('JSON.parse: Unexpected token');
}
while (/\d/.test(currentChar)) {
numStr += currentChar;
position++;
currentChar = jsonString[position];
}
}

if (currentChar === 'e' || currentChar === 'E') {
numStr += currentChar;
position++; // Move past the 'e' or

8. Implement a class that can subscribe to and emit events that trigger attached callback functions.


class EventEmitter {
constructor() {
this.events = {};
}

// Method to subscribe to an event
on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(callback);
}

// Method to emit an event
emit(eventName, ...args) {
const eventCallbacks = this.events[eventName];
if (eventCallbacks) {
eventCallbacks.forEach(callback => {
callback(...args);
});
}
}

// Method to unsubscribe from an event
off(eventName, callback) {
const eventCallbacks = this.events[eventName];
if (eventCallbacks) {
this.events[eventName] = eventCallbacks.filter(cb => cb !== callback);
}
}

// Method to subscribe to an event, but only once
once(eventName, callback) {
const onceCallback = (...args) => {
callback(...args);
this.off(eventName, onceCallback);
};
this.on(eventName, onceCallback);
}
}

// Example usage:
const emitter = new EventEmitter();

// Subscribe to an event
emitter.on('greet', (name) => {
console.log(`Hello, ${name}!`);
});

// Subscribe to an event, but only once
emitter.once('welcome', () => {
console.log('Welcome once!');
});

// Emit events
emitter.emit('greet', 'John');
// Output: Hello, John!

emitter.emit('welcome');
// Output: Welcome once!

emitter.emit('welcome');
// No output, because the 'welcome' event was already emitted once and the subscription was removed.

9. Implement a debounce function that comes with a cancel method to cancel delayed invocations.


function debounce(func, delay) {
let timeoutId;

function debounced(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
}

debounced.cancel = function() {
clearTimeout(timeoutId);
};

return debounced;
}

// Example usage:
function sayHello() {
console.log('Hello!');
}

const debouncedSayHello = debounce(sayHello, 1000);

debouncedSayHello(); // Hello! will be logged after 1000ms
debouncedSayHello(); // If called again before 1000ms, the previous invocation will be canceled
debouncedSayHello(); // If called again before 1000ms, the previous invocation will be canceled

// Cancelling the delayed invocation
debouncedSayHello.cancel(); // The delayed invocation is canceled, 'Hello!' will not be logged

10. Implement a function that recursively flattens an array into a single level deep.


function flattenArray(arr) {
let flattened = [];

arr.forEach(item => {
if (Array.isArray(item)) {
flattened = flattened.concat(flattenArray(item));
} else {
flattened.push(item);
}
});

return flattened;
}

// Example usage:
const nestedArray = [1, [2, [3, 4]], 5, [6]];
const flattenedArray = flattenArray(nestedArray);
console.log(flattenedArray); // Output: [1, 2, 3, 4, 5, 6]

OR


function flattenArray(arr) {
return arr.reduce((flattened, item) => {
return flattened.concat(Array.isArray(item) ? flattenArray(item) : item);
}, []);
}

// Example usage:
const nestedArray = [1, [2, [3, 4]], 5, [6]];
const flattenedArray = flattenArray(nestedArray);
console.log(flattenedArray); // Output: [1, 2, 3, 4, 5, 6]

11. Implement a promisify function that allows the original function to override the return value.


function promisify(originalFunction) {
return function(...args) {
return new Promise((resolve, reject) => {
const callback = (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
};

// Call the original function with the provided arguments and the callback
const returnValue = originalFunction.apply(this, [...args, callback]);

// If the original function returns a value, resolve the promise with that value
if (returnValue !== undefined) {
resolve(returnValue);
}
});
};
}

// Example usage:
function asyncFunction(arg, callback) {
setTimeout(() => {
callback(null, `Processed ${arg}`);
}, 1000);
}

const promisifiedAsyncFunction = promisify(asyncFunction);

promisifiedAsyncFunction("data")
.then(result => {
console.log(result); // Output: Processed data
})
.catch(error => {
console.error(error);
});

// Override the return value of the original function
promisifiedAsyncFunction("data2", () => "Custom return value")
.then(result => {
console.log(result); // Output: Custom return value
})
.catch(error => {
console.error(error);
});

Checkout this article: https://levelup.gitconnected.com/vimp-javascript-promise-implementation-challenges-5a4f120d8606

12. Implement a function to execute N async tasks in series.


function executeTasksInSeries(tasks) {
return new Promise((resolve, reject) => {
const results = [];

// Recursive function to execute tasks in series
function executeNextTask(index) {
if (index >= tasks.length) {
resolve(results);
return;
}

const task = tasks[index];

// Execute the current task
task()
.then(result => {
results.push(result);
// Move to the next task
executeNextTask(index + 1);
})
.catch(error => {
reject(error);
});
}

// Start executing the first task
executeNextTask(0);
});
}

// Example usage:
function asyncTask1() {
return new Promise(resolve => {
setTimeout(() => {
console.log('Task 1 complete');
resolve('Result 1');
}, 1000);
});
}

function asyncTask2() {
return new Promise(resolve => {
setTimeout(() => {
console.log('Task 2 complete');
resolve('Result 2');
}, 1500);
});
}

function asyncTask3() {
return new Promise(resolve => {
setTimeout(() => {
console.log('Task 3 complete');
resolve('Result 3');
}, 2000);
});
}

// Array of async tasks
const tasks = [asyncTask1, asyncTask2, asyncTask3];

// Execute tasks in series
executeTasksInSeries(tasks)
.then(results => {
console.log('All tasks completed:', results);
})
.catch(error => {
console.error('Error:', error);
});

Or using async /await


async function executeTasksInSeries(tasks) {
const results = [];

for (const task of tasks) {
try {
const result = await task();
results.push(result);
} catch (error) {
throw error; // Propagate the error
}
}

return results;
}

// Example usage:
function asyncTask1() {
return new Promise(resolve => {
setTimeout(() => {
console.log('Task 1 complete');
resolve('Result 1');
}, 1000);
});
}

function asyncTask2() {
return new Promise(resolve => {
setTimeout(() => {
console.log('Task 2 complete');
resolve('Result 2');
}, 1500);
});
}

function asyncTask3() {
return new Promise(resolve => {
setTimeout(() => {
console.log('Task 3 complete');
resolve('Result 3');
}, 2000);
});
}

// Array of async tasks
const tasks = [asyncTask1, asyncTask2, asyncTask3];

// Execute tasks in series
executeTasksInSeries(tasks)
.then(results => {
console.log('All tasks completed:', results);
})
.catch(error => {
console.error('Error:', error);
});

Checkout this article: https://levelup.gitconnected.com/vimp-javascript-promise-implementation-challenges-5a4f120d8606

13. Implement a function to execute N async tasks in parallel.

Checkout this article: https://levelup.gitconnected.com/vimp-javascript-promise-implementation-challenges-5a4f120d8606

You can use Promise.all() to execute multiple asynchronous tasks in parallel.


async function executeTasksInParallel(tasks) {
try {
// Execute all tasks concurrently using Promise.all()
const results = await Promise.all(tasks.map(task => task()));
return results;
} catch (error) {
// Handle errors if any task fails
console.error('Error executing tasks:', error);
throw error;
}
}

// Example usage:
async function asyncTask1() {
// Simulate an asynchronous task
return new Promise(resolve => {
setTimeout(() => resolve('Task 1 completed'), 1000);
});
}

async function asyncTask2() {
// Simulate another asynchronous task
return new Promise(resolve => {
setTimeout(() => resolve('Task 2 completed'), 2000);
});
}

async function asyncTask3() {
// Simulate yet another asynchronous task
return new Promise(resolve => {
setTimeout(() => resolve('Task 3 completed'), 1500);
});
}

// Array of asynchronous tasks
const tasks = [asyncTask1, asyncTask2, asyncTask3];

// Execute tasks in parallel
executeTasksInParallel(tasks)
.then(results => {
console.log('All tasks completed successfully:', results);
})
.catch(error => {
console.error('Error executing tasks in parallel:', error);
});

OR


if (!Promise.all) {
Promise.all = function(promises) {
return new Promise(function(resolve, reject) {
if (!Array.isArray(promises)) {
return reject(new TypeError('arguments must be an array'));
}

var results = [];
var completedPromises = 0;

promises.forEach(function(promise, index) {
Promise.resolve(promise)
.then(function(result) {
results[index] = result;
completedPromises++;

// If all promises are completed, resolve with the results
if (completedPromises === promises.length) {
resolve(results);
}
})
.catch(function(error) {
// If any promise fails, reject with the error
reject(error);
});
});

// If the array of promises is empty, resolve immediately
if (promises.length === 0) {
resolve(results);
}
});
};
}

// Example usage:
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = new Promise((resolve) => setTimeout(() => resolve(3), 1000));

Promise.all([promise1, promise2, promise3])
.then((values) => console.log(values))
.catch((error) => console.error(error));

14. Implement a function to execute N async tasks in race.

Checkout this article: https://levelup.gitconnected.com/vimp-javascript-promise-implementation-challenges-5a4f120d8606


async function executeTasksInRace(tasks) {
try {
// Execute all tasks in a race using Promise.race()
const result = await Promise.race(tasks.map(task => task()));
return result;
} catch (error) {
// Handle errors if any task fails
console.error('Error executing tasks in race:', error);
throw error;
}
}

// Example usage:
async function asyncTask1() {
// Simulate an asynchronous task
return new Promise(resolve => {
setTimeout(() => resolve('Task 1 completed'), 1000);
});
}

async function asyncTask2() {
// Simulate another asynchronous task
return new Promise((resolve, reject) => {
setTimeout(() => reject('Task 2 failed'), 2000);
});
}

async function asyncTask3() {
// Simulate yet another asynchronous task
return new Promise(resolve => {
setTimeout(() => resolve('Task 3 completed'), 1500);
});
}

// Array of asynchronous tasks
const tasks = [asyncTask1, asyncTask2, asyncTask3];

// Execute tasks in a race
executeTasksInRace(tasks)
.then(result => {
console.log('First task completed:', result);
})
.catch(error => {
console.error('Error executing tasks in race:', error);
});

OR


if (!Promise.race) {
Promise.race = function(promises) {
return new Promise(function(resolve, reject) {
if (!Array.isArray(promises)) {
return reject(new TypeError('arguments must be an array'));
}

promises.forEach(function(promise) {
Promise.resolve(promise)
.then(function(value) {
// Resolve with the first resolved promise value
resolve(value);
})
.catch(function(error) {
// Reject with the first encountered error
reject(error);
});
});
});
};
}

// Example usage:
const promise1 = new Promise((resolve) => setTimeout(() => resolve('Task 1 completed'), 1000));
const promise2 = new Promise((resolve) => setTimeout(() => resolve('Task 2 completed'), 2000));
const promise3 = new Promise((resolve) => setTimeout(() => resolve('Task 3 completed'), 1500));

Promise.race([promise1, promise2, promise3])
.then((value) => console.log('First task completed:', value))
.catch((error) => console.error('Error:', error));

15. Implement a pipe function which chains N number of functions.

checkout this article: https://levelup.gitconnected.com/javascript-function-chaining-interview-question-7a50be23b42f


class Pipe {
constructor() {
this.functions = [];
}

// Method to add a function to the pipe
add(func) {
this.functions.push(func);
return this; // For chaining
}

// Method to execute the pipe
execute(input) {
return this.functions.reduce((result, func) => func(result), input);
}
}

// Example usage:
const pipe = new Pipe();

// Add functions to the pipe
pipe.add(value => value * 2);
pipe.add(value => value + 3);
pipe.add(value => value - 5);

// Execute the pipe with an initial value
const result = pipe.execute(10); // Output: ((10 * 2) + 3) - 5 = 18
console.log('Result:', result);

16. Implement negative indexing in Array using Proxies. [In progress]

17. Implement Lodash_.get method which gets value from the path.


function get(object, path, defaultValue) {
// Split the path into an array of keys
const keys = Array.isArray(path) ? path : path.split('.');

// Iterate through the keys to access nested properties
for (const key of keys) {
// If the current property is not found, return the default value
if (!object || typeof object !== 'object' || !(key in object)) {
return defaultValue;
}

// Move to the next nested property
object = object[key];
}

// Return the value found at the end of the path
return object;
}

// Example usage:
const obj = {
a: {
b: {
c: 123
}
}
};

console.log(get(obj, 'a.b.c')); // Output: 123
console.log(get(obj, ['a', 'b', 'c'])); // Output: 123
console.log(get(obj, 'a.b.d', 'Not found')); // Output: Not found
console.log(get(obj, 'x.y.z', 'Not found')); // Output: Not found

18. Implement your custom version of the call method which sets the “this” context.

Checkout this article: https://medium.com/@sonikamaheshwari067/javascript-call-apply-bind-b4798bb58d8b


Function.prototype.myCall = function(currentContext = {}, ...arg) {

if (typeof this !== 'function') {
throw new Error(this + "it's not callable");
}
currentContext.fn = this;
currentContext.fn(...arg);
};

Example:
function greet(greeting, punctuation) {
console.log(greeting + ", " + this.name + punctuation);
}
const person = {
name: 'John'
};

greet.call(person, 'Hello', '!'); // Outputs: "Hello, John!"

19. Implement throttling of promises which throttles API requests to max limit.

Checkout this article: https://levelup.gitconnected.com/vimp-javascript-promise-implementation-challenges-5a4f120d8606


function fetchWithAutoRetry(promise, maximumRetryCount, currentRetryCount = 0) {
return promise().catch(error => {
if (currentRetryCount < maximumRetryCount) {
const delay = Math.pow(2, currentRetryCount) * 100;
console.log(`Retrying (${currentRetryCount + 1}/${maximumRetryCount})...`);
return new Promise(resolve =>
setTimeout(resolve, delay)
).then(() =>
fetchWithAutoRetry(promise, maximumRetryCount, currentRetryCount + 1)
);
} else {
throw error; // Reject with the last error if maximum retry count is reached
}
});
}

// Example usage:
const url = 'https://api.example.com/data';

// Simulating a failing fetch with a promise
function simulateFailingFetch() {
return Promise.reject('Failed to fetch data');
}

// Using fetchWithAutoRetry with a maximum retry count of 3
fetchWithAutoRetry(simulateFailingFetch, 3)
.then(data => {
console.log('Data fetched successfully:', data);
})
.catch(error => {
console.error('Max retries reached. Last error:', error);
});

OR


class PromiseThrottler {
constructor(maxRequests, interval) {
this.maxRequests = maxRequests; // Maximum number of requests allowed
this.interval = interval; // Interval (in milliseconds) between each request
this.queue = []; // Queue to hold promises waiting to be executed
this.running = 0; // Number of promises currently executing
}

// Method to add a promise to the queue
add(promiseFunc) {
return new Promise((resolve, reject) => {
// Function to execute the promise
const executePromise = async () => {
try {
const result = await promiseFunc();
resolve(result);
} catch (error) {
reject(error);
} finally {
// Decrease the count of running promises
this.running--;

// Check if there are more promises in the queue to execute
if (this.queue.length > 0) {
const nextPromise = this.queue.shift();
this.runPromise(nextPromise);
}
}
};

// Add the promise function to the queue
this.queue.push(executePromise);

// Check if the number of running promises is less than the maximum allowed
if (this.running < this.maxRequests) {
const nextPromise = this.queue.shift();
this.runPromise(nextPromise);
}
});
}

// Method to run a promise
runPromise(promiseFunc) {
// Increase the count of running promises
this.running++;

// Execute the promise function
promiseFunc();

// Schedule the next promise to run after the interval
setTimeout(() => {
if (this.queue.length > 0) {
const nextPromise = this.queue.shift();
this.runPromise(nextPromise);
}
}, this.interval);
}
}

// Example usage:
const throttler = new PromiseThrottler(2, 1000); // Allow 2 requests per second

// Function representing an API request
const apiRequest = async () => {
// Simulate an API request
await new Promise(resolve => setTimeout(resolve, 500)); // Simulating API request time

console.log('API request completed');
};

// Queue up multiple API requests
throttler.add(apiRequest);
throttler.add(apiRequest);
throttler.add(apiRequest);
throttler.add(apiRequest);

OR, without queue


class PromiseThrottler {
constructor(maxRequests, interval) {
this.maxRequests = maxRequests; // Maximum number of requests allowed
this.interval = interval; // Interval (in milliseconds) between each request
this.running = 0; // Number of promises currently executing
}

// Method to add a promise to be executed
add(promiseFunc) {
return new Promise((resolve, reject) => {
// Function to execute the promise
const executePromise = async () => {
try {
const result = await promiseFunc();
resolve(result);
} catch (error) {
reject(error);
} finally {
// Decrease the count of running promises
this.running--;

// If there are more promises waiting to be executed, run them after the interval
if (this.running < this.maxRequests) {
const nextPromise = this.queue.shift();
if (nextPromise) {
this.runPromise(nextPromise);
}
}
}
};

// Increase the count of running promises
this.running++;

// Execute the promise function
executePromise();
});
}

// Method to run a promise
runPromise(promiseFunc) {
// Execute the promise function
promiseFunc();

// Schedule the next promise to run after the interval
setTimeout(() => {
if (this.running < this.maxRequests) {
const nextPromise = this.queue.shift();
if (nextPromise) {
this.runPromise(nextPromise);
}
}
}, this.interval);
}
}

// Example usage:
const throttler = new PromiseThrottler(2, 1000); // Allow 2 requests per second

// Function representing an API request
const apiRequest = async () => {
// Simulate an API request
await new Promise(resolve => setTimeout(resolve, 500)); // Simulating API request time

console.log('API request completed');
};

// Queue up multiple API requests
throttler.add(apiRequest);
throttler.add(apiRequest);
throttler.add(apiRequest);
throttler.add(apiRequest);

--

--