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 of
  • promise2:Promise.prototype.thenReturn to the promise of
  • user promsie: called by the userthenMethod, 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)