├── myBind.js ├── myCall.js ├── myApply.js ├── customPop.js ├── customLastIndexOf.js ├── customForEach.js ├── customAt.js ├── customPush.js ├── customFindIIndex.js ├── customFind.js ├── customFindIndex.js ├── customFindLastIndex.js ├── customMap.js ├── customEvery.js ├── customSome.js ├── customFilter.js ├── customUnshift.js ├── customReduce.js ├── localStorageWithExpiryTime.js ├── setTimeout.js ├── asyncParallel.js └── customPromises.js /myBind.js: -------------------------------------------------------------------------------- 1 | function myBind(context, ...args1) { 2 | if (typeof this !== 'function') { 3 | throw new TypeError(`${this} is not a function`); 4 | } 5 | 6 | return function(...args2) { 7 | return this.apply(context, [...args1, ...args2]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /myCall.js: -------------------------------------------------------------------------------- 1 | function myCall(scope, ...args) { 2 | if (typeof this !== 'function') { 3 | throw new TypeError(`${this} is not a function`); 4 | } 5 | 6 | scope.fn = this; 7 | return scope.fn(...args); 8 | } 9 | 10 | Function.prototype.myCall = myCall; 11 | -------------------------------------------------------------------------------- /myApply.js: -------------------------------------------------------------------------------- 1 | function myApply(context, args) { 2 | if (typeof this !== 'function') { 3 | throw new TypeError(`${this} is not a function`); 4 | } 5 | 6 | if (!Array.isArray(args)) { 7 | throw new TypeError(`${args} is not an array`); 8 | } 9 | 10 | context.fn = this; 11 | return context.fn(...args); 12 | } 13 | 14 | Function.prototype.myApply = myApply; 15 | -------------------------------------------------------------------------------- /customPop.js: -------------------------------------------------------------------------------- 1 | function customPop() { 2 | if (this == undefined || this === null) { 3 | throw new TypeError(` 4 | Array.prototype.customPop called on null or undefined. 5 | `); 6 | } 7 | 8 | const length = this.length; 9 | if (length === 0) return undefined; 10 | 11 | const lastElement = this[length - 1]; 12 | delete this[length - 1]; 13 | this.length--; 14 | 15 | return lastElement; 16 | } 17 | 18 | Array.prototype.customPop = customPop; 19 | -------------------------------------------------------------------------------- /customLastIndexOf.js: -------------------------------------------------------------------------------- 1 | function customLastIndexOf(searchElement) { 2 | if (this === undefined || this === null) { 3 | throw new TypeError(`Array.prototype.customLastIndexOf called on null or undefined`); 4 | } 5 | 6 | const fromIndex = arguments[1] || this.length - 1; 7 | 8 | for (let i = fromIndex; i >= 0; i--) { 9 | if (this[i] === searchElement) return i; 10 | } 11 | 12 | return -1; 13 | } 14 | 15 | Array.prototype.customLastIndexOf = customLastIndexOf; 16 | -------------------------------------------------------------------------------- /customForEach.js: -------------------------------------------------------------------------------- 1 | function customForEach(callback, context) { 2 | if (this === undefined || this === null) { 3 | throw new TypeError(`Array.prototype.forEach() called on null or undefined`); 4 | } 5 | 6 | if (!callback && typeof callback !== 'function') { 7 | throw new TypeError(`${callback} is not a function`); 8 | } 9 | 10 | const length = this.length; 11 | for (let i = 0; i < length; i++) { 12 | callback.call(context, this[i], i, this); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /customAt.js: -------------------------------------------------------------------------------- 1 | function customAt() { 2 | if (this === undefined || this === null) { 3 | throw new TypeError(`Array.prototype.customAt called at null or undefined`); 4 | } 5 | 6 | const length = this.length; 7 | const argumentsLength = arguments.length; 8 | if (!argumentsLength) return length; 9 | 10 | const index = arguments[0]; 11 | if (index >= 0) return this[index]; 12 | else return this[this.length + index]; 13 | } 14 | 15 | Array.prototype.customAt = customAt; 16 | -------------------------------------------------------------------------------- /customPush.js: -------------------------------------------------------------------------------- 1 | function customPush() { 2 | if (this === undefined || this === null) { 3 | throw new TypeError( 4 | `Array.prototype.customPush called on null or undefined` 5 | ); 6 | } 7 | 8 | const argumentsLength = arguments.length; 9 | const length = this.length; 10 | 11 | for (let i = 0; i < argumentsLength; i++) { 12 | this[i + length] = arguments[i]; 13 | } 14 | 15 | this.length = length + argumentsLength; 16 | return this.length; 17 | } 18 | 19 | Array.prototype.customPush = customPush; 20 | -------------------------------------------------------------------------------- /customFindIIndex.js: -------------------------------------------------------------------------------- 1 | function customFindLast(callback, context) { 2 | if (this === undefined || this === null) { 3 | throw new TypeError(`Array.prototype.customFindLast called on null or undefined`); 4 | } 5 | 6 | if (!callback && typeof callback !== 'function') { 7 | throw new TypeError(`${callback} is not a function`); 8 | } 9 | 10 | for (let i = this.length - 1; i >= 0; i--) { 11 | if (callback.call(context, this[i], i, this)) return this[i]; 12 | } 13 | } 14 | 15 | Array.prototype.customFindLast = customFindLast; 16 | -------------------------------------------------------------------------------- /customFind.js: -------------------------------------------------------------------------------- 1 | function customFind(callback) { 2 | 'use strict'; 3 | 4 | if (this === undefined || this === null) { 5 | throw new TypeError(`Array.prototype.customFind called on null or undefined`); 6 | } else if (typeof callback !== 'function') { 7 | throw new TypeError(`callback must be a function`); 8 | } 9 | 10 | for (let i = 0; i < this.length; i++) { 11 | if (callback(this[i], i, this)) { 12 | return this[i]; 13 | } 14 | } 15 | 16 | return undefined; 17 | } 18 | 19 | Array.prototype.customFind = customFind; 20 | -------------------------------------------------------------------------------- /customFindIndex.js: -------------------------------------------------------------------------------- 1 | function customFindIndex(callback, context) { 2 | if (this === undefined || this === null) { 3 | throw new TypeError(`Array.prototype.customFindIndex called on null or undefined`); 4 | } 5 | 6 | if (!callback && typeof callback !== 'function') { 7 | throw new TypeError(`${callback} is not a function`); 8 | } 9 | 10 | for (let i = 0; i < this.length; i++) { 11 | if (callback.call(context, this[i], i, this)) return i; 12 | } 13 | 14 | return -1; 15 | } 16 | 17 | Array.prototype.customFindIndex = customFindIndex; 18 | -------------------------------------------------------------------------------- /customFindLastIndex.js: -------------------------------------------------------------------------------- 1 | function customFindLastIndex(callback, context) { 2 | if (this === undefined || this === null) { 3 | throw new TypeError(`Array.prototype.customFindLastIndex called on null or undefined`); 4 | } 5 | 6 | if (typeof callback !== 'function') { 7 | throw new TypeError(`${callback} is not a function`); 8 | } 9 | 10 | for (let i = this.length - 1; i >= 0; i--) { 11 | if (callback.call(context, this[i], i, this)) return i; 12 | } 13 | 14 | return -1; 15 | } 16 | 17 | Array.prototype.customFindLastIndex = customFindLastIndex; 18 | -------------------------------------------------------------------------------- /customMap.js: -------------------------------------------------------------------------------- 1 | function customMap(callback, context) { 2 | if (this === undefined || this === null) { 3 | throw new TypeError(`Array.prototype.customMap() called on null or undefined`); 4 | } 5 | 6 | if (!callback && typeof callback !== 'function') { 7 | throw new TypeError(`${callback} is not a function`); 8 | } 9 | 10 | const result = []; 11 | const length = this.length; 12 | 13 | for (let i = 0; i < length; i++) { 14 | if (i in this) { 15 | result.push(callback.call(context, this[i], i, this)); 16 | } 17 | } 18 | 19 | return result; 20 | } 21 | -------------------------------------------------------------------------------- /customEvery.js: -------------------------------------------------------------------------------- 1 | function customEvery(callback) { 2 | if (this === undefined || this === null) { 3 | throw new TypeError(`Array.prototype.customEvery called on null or undefined`); 4 | } else if (typeof callback !== 'function') { 5 | throw new TypeError(`callback is not a function`); 6 | } 7 | 8 | const thisArg = arguments[1] || this; 9 | 10 | for (let i = 0; i < this.length; i++) { 11 | if (i in this && !callback.call(thisArg, this[i], i, this)) { 12 | return false; 13 | } 14 | } 15 | 16 | return true; 17 | } 18 | 19 | Array.prototype.customEvery = customEvery; 20 | -------------------------------------------------------------------------------- /customSome.js: -------------------------------------------------------------------------------- 1 | function customSome(callback) { 2 | if (this === undefined || this === null) { 3 | throw new TypeError(`Array.prototype.customSome called on null or undefined`); 4 | } else if (typeof callback !== 'function') { 5 | throw new TypeError(`callback should be a function`); 6 | } 7 | 8 | const thisArg = arguments[1] || this; 9 | 10 | for (let i = 0; i < this.length; i++) { 11 | if (i in this && callback.call(thisArg, this[i], i, this)) { 12 | return true; 13 | } 14 | } 15 | 16 | return false; 17 | } 18 | 19 | Array.prototype.customSome = customSome; 20 | -------------------------------------------------------------------------------- /customFilter.js: -------------------------------------------------------------------------------- 1 | function customFilter(callback, context) { 2 | if (this === undefined || this === null) { 3 | throw new TypeError(`Array.prototype.customFilter() called on null or undefined`); 4 | } 5 | 6 | if (!callback && typeof callback !== 'function') { 7 | throw new TypeError(`${callback} is not a function`); 8 | } 9 | 10 | const length = this.length; 11 | const result = []; 12 | 13 | for (let i = 0; i < length; i++) { 14 | if (i in this && callback.call(context, this[i], i, this)) { 15 | result.push(this[i]); 16 | } 17 | } 18 | 19 | return result; 20 | } 21 | -------------------------------------------------------------------------------- /customUnshift.js: -------------------------------------------------------------------------------- 1 | function customUnshift() { 2 | if (this === undefined || this === null) { 3 | throw new TypeError(`Array.prototype.customUnshift called on null or undefined`); 4 | } 5 | 6 | const length = this.length || 0; 7 | const argumentsLength = arguments.length; 8 | 9 | if (!argumentsLength) return length; 10 | 11 | for (let i = length - 1; i >= 0; i--) { 12 | if (i in this) { 13 | this[i + argumentsLength] = this[i]; 14 | } 15 | delete this[i]; 16 | } 17 | 18 | for (let i = 0; i < argumentsLength; i++) { 19 | this[i] = arguments[i]; 20 | } 21 | 22 | this.length = length + argumentsLength; 23 | return this.length; 24 | } 25 | 26 | Array.prototype.customUnshift = customUnshift; 27 | -------------------------------------------------------------------------------- /customReduce.js: -------------------------------------------------------------------------------- 1 | function customReduce(callback, initialValue) { 2 | if (this === undefined || this === null) { 3 | throw new TypeError(`Array.prototype.customReduce called on null or undefined`); 4 | } 5 | 6 | if (!callback && typeof callback !== 'function') { 7 | throw new TypeError(`${callback} is not a function`); 8 | } 9 | 10 | if (!this.length) { 11 | if (arguments.length < 2) { 12 | throw new TypeError(`Reduce of empty array with no initial value`); 13 | } else if (arguments.length === 2) { 14 | return initialValue; 15 | } 16 | } 17 | 18 | let acc = initialValue || 0; 19 | for (let i = 0; i < this.length; i++) { 20 | acc = callback(acc, this[i], i, this); 21 | } 22 | return acc; 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /localStorageWithExpiryTime.js: -------------------------------------------------------------------------------- 1 | window.myLocalStorage = { 2 | getItem(key) { 3 | let result = JSON.parse(window.localStorage.getItem(key)); 4 | 5 | if (result) { 6 | if (result.expiryTime <= Date.now()) { 7 | window.localStorage.removeItem(result.key); 8 | return null; 9 | } 10 | return result.data; 11 | } 12 | 13 | return null; 14 | }, 15 | 16 | setItem(key, value, maxAge = 30 * 30 * 60 * 1000) { 17 | let result = { data: value }; 18 | 19 | if (maxAge) { 20 | result.expiryTime = Date.now() + maxAge; 21 | } 22 | 23 | window.localStorage.setItem(key, JSON.stringify(result)); 24 | } 25 | } 26 | 27 | window.myLocalStorage.setItem("hello", "world", 1000); 28 | setTimeout(() => { 29 | console.log(window.myLocalStorage.getItem("hello")); 30 | }, 2000); 31 | -------------------------------------------------------------------------------- /setTimeout.js: -------------------------------------------------------------------------------- 1 | function createCustomSetTimeout() { 2 | let map = {}; 3 | let timerId = 0; 4 | 5 | function setTimeoutPolyfill(callback, delay, ...args) { 6 | timerId++; 7 | map[timerId] = true; 8 | const date = Date.now(); 9 | 10 | function triggerCallback() { 11 | if (!map[timerId]) return; 12 | if (Date.now() > date + delay) { 13 | callback.apply(this, args); 14 | } else { 15 | requestAnimationFrame(triggerCallback); 16 | } 17 | } 18 | 19 | requestAnimationFrame(triggerCallback); 20 | return timerId; 21 | } 22 | 23 | function clearTimeoutPolyfill(timerId) { 24 | if (map[timerId]) delete map[timerId]; 25 | } 26 | 27 | return { setTimeoutPolyfill, clearTimeoutPolyfill }; 28 | } 29 | 30 | const { setTimeoutPolyfill, clearTimeoutPolyfill } = createCustomSetTimeout(); 31 | 32 | console.log("1") 33 | setTimeout((name) => console.log("Hello", name), 100, "Walter") 34 | console.log("2") 35 | -------------------------------------------------------------------------------- /asyncParallel.js: -------------------------------------------------------------------------------- 1 | function createAsyncTask() { 2 | const value = Math.floor(Math.random() * 9); 3 | return new Promise((resolve, reject) => { 4 | setTimeout(() => { 5 | if (value < 5) { 6 | reject(value) 7 | } else { 8 | resolve(value); 9 | } 10 | }, value * 1000); 11 | }) 12 | } 13 | 14 | const asyncTasksList = [ 15 | createAsyncTask(); 16 | createAsyncTask(); 17 | createAsyncTask(); 18 | createAsyncTask(); 19 | createAsyncTask(); 20 | ] 21 | 22 | function executeAsyncTasksInParallel(asyncTasks, callback) { 23 | const results = []; 24 | const errors = []; 25 | let completed = 0; 26 | 27 | asyncTasks.forEach(asyncTask => { 28 | asyncTask 29 | .then(res => results.push(res)); 30 | .catch(err => errors.push(err)); 31 | .finally(() => { 32 | completed++; 33 | if (completed >= asyncTasks.length) { 34 | callback(results, errors); 35 | } 36 | }) 37 | }) 38 | } 39 | 40 | executeAsyncTasksInParallel(asyncTasksList, (data, errors) => { 41 | console.log(data, errors); 42 | }) 43 | -------------------------------------------------------------------------------- /customPromises.js: -------------------------------------------------------------------------------- 1 | // Polyfill to implement custom promises 2 | 3 | class MyPromise { 4 | resolvedData; 5 | resolveChain = []; 6 | isResolved = false; 7 | 8 | rejectedData; 9 | rejectChain = []; 10 | isRejected = false; 11 | 12 | constructor(executor) { 13 | const resolve = (value) => { 14 | this.isResolved = true; 15 | this.resolvedData = value; 16 | 17 | if (this.resolveChain.length) { 18 | this.resolveChain.reduce((acc, fn) => fn(acc), this.resolvedData); 19 | } 20 | }; 21 | 22 | const reject = (value) => { 23 | this.rejectedData = value; 24 | this.isRejected = true; 25 | 26 | if (this.rejectChain.length) { 27 | this.rejectChain.reduce((acc, fn) => fn(acc), this.rejectedData); 28 | } 29 | }; 30 | 31 | try { 32 | executor(resolve, reject); 33 | } catch (error) { 34 | reject(error); 35 | } 36 | } 37 | 38 | then(fn) { 39 | this.resolveChain.push(fn); 40 | if (this.isResolved) { 41 | this.resolveChain.reduce((acc, fn) => fn(acc), this.resolvedData); 42 | } 43 | return this; 44 | } 45 | 46 | catch(fn) { 47 | this.rejectChain.push(fn); 48 | if (this.isRejected) { 49 | this.rejectChain.reduce((acc, fn) => fn(acc), this.rejectedData); 50 | } 51 | return this; 52 | } 53 | 54 | finally(fn) { 55 | this.resolveChain.push(fn); 56 | this.rejectChain.push(fn); 57 | 58 | if (this.isResolved) { 59 | this.resolveChain.reduce((acc, fn) => fn(acc), this.resolvedData); 60 | } 61 | 62 | if (this.isRejected) { 63 | this.rejectChain.reduce((acc, fn) => fn(acc), this.rejectedData); 64 | } 65 | } 66 | 67 | static resolve(value) { 68 | return new MyPromise((resolve) => resolve(value)); 69 | } 70 | 71 | static reject(value) { 72 | return new Promise((_, reject) => reject(value)); 73 | } 74 | 75 | static all(promises) { 76 | const results = []; 77 | let count = 0; 78 | 79 | return new MyPromise((resolve, reject) => { 80 | for (let i = 0; i < promises.length; i++) { 81 | promises[i] 82 | .then((data) => { 83 | results[i] = data; 84 | count++; 85 | 86 | if (count === promises.length) { 87 | resolve(results); 88 | } 89 | }) 90 | .catch((error) => reject(error)); 91 | } 92 | }); 93 | } 94 | 95 | static race(promises) { 96 | return new MyPromise((resolve, reject) => { 97 | promises.forEach((promise) => { 98 | return promise.then(resolve).catch(reject); 99 | }); 100 | }); 101 | } 102 | 103 | static allSettled(promises) { 104 | const result = []; 105 | let count = 0; 106 | 107 | return new Promise((resolve) => { 108 | const handleResult = (value, index, status) => { 109 | result[index] = { status, value }; 110 | count++; 111 | if (count === promises.length) resolve(result); 112 | }; 113 | 114 | for (let i = 0; i < promises.length; i++) { 115 | promises[i] 116 | .then((res) => handleResult(res, i, "fulfilled")) 117 | .catch((err) => handleResult(err, i, "rejected")); 118 | } 119 | }); 120 | } 121 | 122 | static any(promises) { 123 | const result = []; 124 | let count = 0; 125 | 126 | return new Promise((resolve, reject) => { 127 | for (let i = 0; i < promises.length; i++) { 128 | promises[i].then(resolve).catch((err) => { 129 | result[index] = { status: "rejected", value: err }; 130 | count++; 131 | if (count === promises.length) reject(result); 132 | }); 133 | } 134 | }); 135 | } 136 | } 137 | 138 | // MyPromise.all([ 139 | // Promise.resolve(10), 140 | // Promise.resolve(20), 141 | // Promise.reject("Error"), 142 | // Promise.resolve(40), 143 | // ]).then((data) => console.log(data)); 144 | // MyPromise.resolve(10).then((data) => console.log(data)); 145 | // MyPromise.reject("Error").catch((data) => console.log(data)); 146 | 147 | // new MyPromise((resolve, reject) => { 148 | // setTimeout(() => { 149 | // resolve(100); 150 | // }, 100); 151 | // setTimeout(() => { 152 | // reject("Async Error!!"); 153 | // }, 100); 154 | // resolve(10); 155 | // reject("Error!"); 156 | // }); 157 | // .then((data) => { 158 | // return data * 2; 159 | // }) 160 | // .then((data) => { 161 | // console.log(data); 162 | // }) 163 | // .catch((err) => err) 164 | // .catch((err) => console.log(err)); 165 | // .finally((data) => console.log(data)); 166 | 167 | // Edge case 168 | // 1. Don't allow multiple resolve and reject returns 169 | // 2. Return new promise from .then() or .catch() 170 | // 3. Only return error if the .any() any of the promise fails 171 | --------------------------------------------------------------------------------