This is the fourth day of my participation in the August Text Challenge.More challenges in August
Interviews often require us to handwritten code, understanding the implementation of these handwritten code is also conducive to increasing our technical precipitation, today we will take a look at these common handwritten code implementation
Promise
Importance: ⭐⭐⭐⭐⭐
Handwriting frequency: ⭐⭐
Promise is a very important knowledge point, we must master, but we need to handwritten Promise situation is not much, more is to test our understanding of Promise
The following promise implementation source is SSH big guy’s simplest implementation promise, support asynchronous chain call (20 lines), I made some changes
function Promise(exec) {
this.cbs = []
const resolve = value= > {
setTimeout(() = > {
this.data = value
this.cbs.forEach(cb= > cb())
})
}
exec(resolve)
}
Promise.prototype.then = function (onResolved) {
return new Promise(resolve= > {
this.cbs.push(() = > {
const res = onResolved(this.data)
res instanceof Promise ? res.then(resolve) : resolve(res)
})
})
}
Copy the code
Let’s analyze the principle
The first is the constructor
function Promise(exec) {
this.cbs = []
const resolve = value= > {
setTimeout(() = > {
this.data = value
this.cbs.forEach(cb= > cb())
})
}
exec(resolve)
}
Copy the code
First use CBS to save the set of callback functions for Promise Resolve
This is to address the case of calling the THEN method multiple times on a promise, as follows
const promise = new Promise()
promise.then()
Copy the code
Then resolve, where setTimeout is used to simulate asynchronous execution of functions in THEN. Of course setTimeout belongs to task, promise.then () belongs to Micro Task, so the actual performance is a little different, but we’re not focusing on that. Ignore the problem
The functions in CBS are then executed in sequence in the setTimeout callback
Finally, execute the user function exec and pass resolve
Then to analyze the implementation of then, this piece is heavy in the middle, the key to the chain call, sit back, ready to take off ✈️!
Promise.prototype.then = function (onResolved) {
return new Promise(resolve= > {
this.cbs.push(() = > {
const res = onResolved(this.data)
res instanceof Promise ? res.then(resolve) : resolve(res)
})
})
}
Copy the code
Let’s define a few aliases for the rest of our discussion
promise1
:new Promise()
Return to the promise ofpromise2
:Promise.prototype.then
Return to the promise ofuser promsie
: called by the userthen
Method, the user manually constructs a promise and returns the promise, i.eres
(May be promise)
Okay, so let’s start analyzing, and notice that this in then refers to promise1
First, let’s make it clear that the THEN method is a method on a Promise instance, so we need to return a new Promise instance in the THEN method in order to be able to chain
In promise2, the incoming function of promise2 performs this.cbs.push(), pushing a function into CBS for subsequent execution
Let’s focus on the push function, which will not execute until promise1 is resolved
;() = > {
const res = onResolved(this.data)
res instanceof Promise ? res.then(resolve) : resolve(res)
}
Copy the code
OnResolved corresponds to the function passed in by then. If the user returns a user promise, resolve promise2 will be resolved when appropriate
That’s this piece of logic
res instanceof Promise ? res.then(resolve)
Copy the code
Transfer the resolve of promise2 to the User Promise
Consider the following example:
new Promise(resolve= > {
setTimeout(() = > {
// resolve1
resolve(1)},500)})// then1
.then(res= > {
console.log(res)
// user promise
return new Promise(resolve= > {
setTimeout(() = > {
// resolve2
resolve(2)},500)})})// then2
.then(console.log)
Copy the code
Then1 returns promise2, so then2 is essentially promise2. Then (console.log),
If resolve2 is called, the user promise will be resolved, and then the user promise will be resolved. If resolve2 is called, the user promise will be resolved. The CBS array in promise2 is triggered in turn.
After the resolve2 is completed, the logic in then2 will continue to execute, i.e. the asynchronous chain call.
Deep copy
Importance: ⭐⭐⭐
Handwriting frequency: ⭐⭐⭐
In development, we often use some simple methods to deeply copy objects, such as
constcloneObj = { ... rawObj }Copy the code
However, in the interview, the interviewer will not be satisfied with this answer, and this approach does have some problems, such as the inability to copy the nested object situation
const nestedObj = {
name: 'nested',}const rawObj = {
name: 'raw',
nestedObj,
}
constcloneObj = { ... rawObj } cloneObj.nestedObj.name ='clone'
console.log(cloneObj, nestedObj)
// { name: 'raw', nestedObj: { name: 'clone' } } { name: 'clone' }
Copy the code
We need to write a deep copy that solves the above problems
The following code
function deepClone(target, map = new Map(a)) {
if (target instanceof Object) {
let cloneTarget = target instanceof Array ? [] : {}
if (map.get(target)) {
return map.get(target)
}
map.set(target, cloneTarget)
for (const key in target) {
cloneTarget[key] = deepClone(target[key], map)
}
return cloneTarget
} else {
return target
}
}
Copy the code
Check whether the target passed in is an object. Use instanceof instead of typeof to avoid identifying null as an object
If it is not an object, it is returned directly, and we do not need to process it, otherwise we enter the following process
We subdivide target into arrays and objects, and cloneTarget starts as an empty array or object
We then use the map to determine if the target already exists in our map. If so, return the object in the map, otherwise add the target to the map
This is done to solve the problem of circular references. WeakMap can also be used for better performance
const rawObj = {
name: 'raw',
}
rawObj.nestedObj = rawObj
const cloneObj = deepClone(rawObj)
console.log(cloneObj)
// { name: 'raw', nestedObj: [Circular *1] }
Copy the code
RangeError: Maximum Call Stack Size exceeded RangeError: Maximum Call Stack size exceeded
Next, we recursively call deepClone over each of target’s keys
Finally, cloneTarget is returned
Test the
const nestedObj = {
name: 'nested',}const rawObj = {
name: 'raw',
nestedObj,
}
const cloneObj = deepClone(rawObj)
cloneObj.nestedObj.name = 'clone'
console.log(cloneObj, nestedObj)
// { name: 'raw', nestedObj: { name: 'clone' } } { name: 'nested' }
Copy the code
If the throttle
Importance: ⭐⭐⭐
Handwriting frequency: ⭐⭐⭐⭐
Anti – shake and throttling are commonly used and often back to review handwritten code, anti – shake and throttling analysis as follows
Buffeting: To execute a function only once after it has been executed several times. This function is often used to click a button to send a request or obtain a search result
Throttling: Execution of a function multiple times, at intervals between events, often used for lazy loading, etc., by listening for wheel events
Let’s look at their implementation
Image stabilization
function debounce(fn, delay) {
let timer = null
return (. args) = > {
clearTimeout(timer)
timer = setTimeout(fn, delay, ... args) } }Copy the code
The throttle
function throttle(fn, delay) {
let canRun = true
return (. args) = > {
if(! canRun)return
canRun = false
setTimeout(() = >{ fn(... args) canRun =true
}, delay)
}
}
Copy the code
These two functions are easy to understand once you understand what you want to accomplish, so I won’t go into them here
Please give me a thumbs up if this article helped you, and I’ll see you next time
In the next installment, I will introduce the implementation of various apis, such as Map, Reduce, Filter, call, etc. Stay tuned
Refer to the article
Implement Promises with minimal support for asynchronous chained calls (20 lines)