7
头图

Preface

(ಥ﹏ಥ) A real event that happened to a friend. The interviewer asked him to write a Promise.all . The friend did not perform well on the scene and did not write it out. Afterwards, he asked the interviewer to give a vague evaluation that the not solid enough. knowledge is less mastered... 161ad52906a7ba Of course, the entire interview failed, not just for this question, there must be other reasons.

But it gave us a wake-up call: Promise handwritten implementation and Promise static method implementation have long been high-frequency exam questions in interviews. If you don’t know it well, it will delay you 10 minutes. Let’s work together until he understands it. O(∩_∩)O

Common Interview Handwriting Series

recently want to do something. I hope to write a series of common handwritten questions front-end interview. Try to explain the knowledge and principles involved. If you are also interested in this series, welcome to come together Learning, 161ad52906a836 66+ handwriting questions present!

1. Click to view the source address of Ri Gong one question (currently 66+ handwritten questions have been implemented)

2. Nuggets column

image.png

Promise.resolve

Brief review

  1. Promise.resolve(value) Promise object parsed with a given value.
  2. If this value is a promise, then this promise will be returned;
  3. If this value is thenable (that is, with the "then"  method), the returned promise will "follow" the thenable object and adopt its final state; otherwise, the returned promise will be completed with this value.

This is the explanation on MDN, let’s look at them one by one

  1. Promise.resolve final result of Promise , and is closely related to Promise.resolve (the value)
  2. The passed-in parameter can be a Promise instance, then the result of the function execution is to directly return the instance
  3. Here the most important need to understand follow , can be understood as Promise final state is the object of this thenable output value

small example


// 1. 非Promise对象,非thenable对象
Promise.resolve(1).then(console.log) // 1

// 2. Promise对象成功状态
const p2 = new Promise((resolve) => resolve(2))

Promise.resolve(p2).then(console.log) // 2

// 3. Promise对象失败状态
const p3 = new Promise((_, reject) => reject('err3'))

Promise.resolve(p3).catch(console.error) // err3

// 4. thenable对象
const p4 = {
  then (resolve) {
    setTimeout(() => resolve(4), 1000)
  }
}
Promise.resolve(p4).then(console.log) // 4

// 5. 啥都没传
Promise.resolve().then(console.log) // undefined

Source code implementation

Promise.myResolve = function (value) {
  // 是Promise实例,直接返回即可
  if (value && typeof value === 'object' && (value instanceof Promise)) {
    return value
  }
  // 否则其他情况一律再通过Promise包装一下 
  return new Promise((resolve) => {
    resolve(value)
  })
}

// 测试一下,还是用刚才的例子
// 1. 非Promise对象,非thenable对象
Promise.myResolve(1).then(console.log) // 1

// 2. Promise对象成功状态
const p2 = new Promise((resolve) => resolve(2))

Promise.myResolve(p2).then(console.log) // 2

// 3. Promise对象失败状态
const p3 = new Promise((_, reject) => reject('err3'))

Promise.myResolve(p3).catch(console.error) // err3

// 4. thenable对象
const p4 = {
  then (resolve) {
    setTimeout(() => resolve(4), 1000)
  }
}
Promise.myResolve(p4).then(console.log) // 4

// 5. 啥都没传
Promise.myResolve().then(console.log) // undefined

question

From the source code implementation, there is no special treatment thenable In fact, it does not need to Promise.resolve . The actual processing should be in the Promise constructor. If you are interested in this piece, you will write Promise immediately. Looking forward to your reading.

Promise.reject

Brief review

Promise.reject() Promise object with a reason for rejection.

Promise.reject(new Error('fail'))
  .then(() => console.log('Resolved'), 
        (err) => console.log('Rejected', err))
// 输出以下内容        
// Rejected Error: fail
//    at <anonymous>:2:16        

Source code implementation

The implementation of reject is relatively simple, just return a new Promise and set the result status to reject.
Promise.myReject = function (value) {
  return new Promise((_, reject) => {
    reject(value)
  })
}

// 测试一下
Promise.myReject(new Error('fail'))
  .then(() => console.log('Resolved'), 
        (err) => console.log('Rejected', err))

// Rejected Error: fail
//    at <anonymous>:9:18

Promise.all

Brief review

Promise.all() method is used to package multiple Promise instances into a new Promise instance. This static method should be the most common in interviews
const p = Promise.all([p1, p2, p3])

The final p state by p1 , p2 , p3 determined, divided into two cases.

(1) Only p1 , p2 , p3 state have become fulfilled , p state will become a fulfilled , this time p1 , p2 , p3 return value consisting of an array, is passed to p callback function.

(2) long p1 , p2 , p3 into a is rejected , p state becomes rejected , the first case is reject return value of the instance, is passed to the p callback function.

const p1 = Promise.resolve(1)
const p2 = new Promise((resolve) => {
  setTimeout(() => resolve(2), 1000)
})
const p3 = new Promise((resolve) => {
  setTimeout(() => resolve(3), 3000)
})

const p4 = Promise.reject('err4')
const p5 = Promise.reject('err5')
// 1. 所有的Promise都成功了
const p11 = Promise.all([ p1, p2, p3 ])
    .then(console.log) // [ 1, 2, 3 ]
      .catch(console.log)
      
// 2. 有一个Promise失败了
const p12 = Promise.all([ p1, p2, p4 ])
    .then(console.log)
      .catch(console.log) // err4
      
// 3. 有两个Promise失败了,可以看到最终输出的是err4,第一个失败的返回值
const p13 = Promise.all([ p1, p4, p5 ])
    .then(console.log)
      .catch(console.log) // err4

Source code implementation


Promise.myAll = (promises) => {
  return new Promise((rs, rj) => {
    // 计数器
    let count = 0
    // 存放结果
    let result = []
    const len = promises.length
    
    if (len === 0) {
      return rs([])
    }
    
    promises.forEach((p, i) => {
      // 注意有的数组项有可能不是Promise,需要手动转化一下
      Promise.resolve(p).then((res) => {
        count += 1
        // 收集每个Promise的返回值 
        result[ i ] = res
        // 当所有的Promise都成功了,那么将返回的Promise结果设置为result
        if (count === len) {
          rs(result)
        }
        // 监听数组项中的Promise catch只要有一个失败,那么我们自己返回的Promise也会失败
      }).catch(rj)
    })
  })
}

// 测试一下
const p1 = Promise.resolve(1)
const p2 = new Promise((resolve) => {
  setTimeout(() => resolve(2), 1000)
})
const p3 = new Promise((resolve) => {
  setTimeout(() => resolve(3), 3000)
})

const p4 = Promise.reject('err4')
const p5 = Promise.reject('err5')
// 1. 所有的Promise都成功了
const p11 = Promise.myAll([ p1, p2, p3 ])
    .then(console.log) // [ 1, 2, 3 ]
      .catch(console.log)
      
// 2. 有一个Promise失败了
const p12 = Promise.myAll([ p1, p2, p4 ])
    .then(console.log)
      .catch(console.log) // err4
      
// 3. 有两个Promise失败了,可以看到最终输出的是err4,第一个失败的返回值
const p13 = Promise.myAll([ p1, p4, p5 ])
    .then(console.log)
      .catch(console.log) // err4
// 与原生的Promise.all返回是一致的    

Promise.allSettled

Brief review

Sometimes, we want to wait until a set of asynchronous operations are over, regardless of whether each operation succeeds or fails, and then proceed to the next step. Obviously Promise.all (as long as one fails, it will enter the failed state as a result) is not suitable, so there is Promise.allSettled
Promise.allSettled() method accepts an array as a parameter, each member of the array is a Promise object, and returns a new Promise object. Only when the state of all Promise objects in the parameter array has changed (whether it is fulfilled or rejected ), the state of the returned Promise object will change. Once the state changes, the state will always be fulfilled and will not become rejected

Take the example above as an example, let’s see what is different Promise.all

const p1 = Promise.resolve(1)
const p2 = new Promise((resolve) => {
  setTimeout(() => resolve(2), 1000)
})
const p3 = new Promise((resolve) => {
  setTimeout(() => resolve(3), 3000)
})

const p4 = Promise.reject('err4')
const p5 = Promise.reject('err5')
// 1. 所有的Promise都成功了
const p11 = Promise.allSettled([ p1, p2, p3 ])
    .then((res) => console.log(JSON.stringify(res, null,  2)))

// 输出 
/*
[
  {
    "status": "fulfilled",
    "value": 1
  },
  {
    "status": "fulfilled",
    "value": 2
  },
  {
    "status": "fulfilled",
    "value": 3
  }
]
*/
      
// 2. 有一个Promise失败了
const p12 = Promise.allSettled([ p1, p2, p4 ])
    .then((res) => console.log(JSON.stringify(res, null,  2)))
        
// 输出 
/*
[
  {
    "status": "fulfilled",
    "value": 1
  },
  {
    "status": "fulfilled",
    "value": 2
  },
  {
    "status": "rejected",
    "reason": "err4"
  }
]
*/
      
// 3. 有两个Promise失败了
const p13 = Promise.allSettled([ p1, p4, p5 ])
    .then((res) => console.log(JSON.stringify(res, null,  2)))
        
// 输出 
/*
[
  {
    "status": "fulfilled",
    "value": 1
  },
  {
    "status": "rejected",
    "reason": "err4"
  },
  {
    "status": "rejected",
    "reason": "err5"
  }
]
*/

can be seen:

  1. Whether all of the success or failure of some, will eventually enter Promise.allSettled of .then callback
  2. Finally, the return value, the success and failure of the item has status property, a time when success is fulfilled , when failure is rejected
  3. In the final return value, success contains the value , and failure is the attribute reason

Source code implementation

Promise.myAllSettled = (promises) => {
  return new Promise((rs, rj) => {
    let count = 0
    let result = []
    const len = promises.length
    // 数组是空的话,直接返回空数据
    if (len === 0) {
      return resolve([])
    }

    promises.forEach((p, i) => {
      Promise.resolve(p).then((res) => {
        count += 1
        // 成功属性设置 
        result[ i ] = {
          status: 'fulfilled',
          value: res
        }
        
        if (count === len) {
          rs(result)
        }
      }).catch((err) => {
        count += 1
        // 失败属性设置 
        result[i] = { 
          status: 'rejected', 
          reason: err 
        }

        if (count === len) {
          rs(result)
        }
      })
    })
  })
}

// 测试一下
const p1 = Promise.resolve(1)
const p2 = new Promise((resolve) => {
  setTimeout(() => resolve(2), 1000)
})
const p3 = new Promise((resolve) => {
  setTimeout(() => resolve(3), 3000)
})

const p4 = Promise.reject('err4')
const p5 = Promise.reject('err5')
// 1. 所有的Promise都成功了
const p11 = Promise.myAllSettled([ p1, p2, p3 ])
    .then((res) => console.log(JSON.stringify(res, null,  2)))

// 输出 
/*
[
  {
    "status": "fulfilled",
    "value": 1
  },
  {
    "status": "fulfilled",
    "value": 2
  },
  {
    "status": "fulfilled",
    "value": 3
  }
]
*/
      
// 2. 有一个Promise失败了
const p12 = Promise.myAllSettled([ p1, p2, p4 ])
    .then((res) => console.log(JSON.stringify(res, null,  2)))
        
// 输出 
/*
[
  {
    "status": "fulfilled",
    "value": 1
  },
  {
    "status": "fulfilled",
    "value": 2
  },
  {
    "status": "rejected",
    "reason": "err4"
  }
]
*/
      
// 3. 有两个Promise失败了
const p13 = Promise.myAllSettled([ p1, p4, p5 ])
    .then((res) => console.log(JSON.stringify(res, null,  2)))
        
// 输出 
/*
[
  {
    "status": "fulfilled",
    "value": 1
  },
  {
    "status": "rejected",
    "reason": "err4"
  },
  {
    "status": "rejected",
    "reason": "err5"
  }
]
*/

Promise.race

Brief review

Promise.race() method also wraps multiple Promise instances into a new Promise instance.

const p = Promise.race([p1, p2, p3])

As long as p1 , p2 , and p3 changes state first, the state of p will change accordingly. The return value of the Promise instance that was changed first is passed to the callback function of p

const p1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 1)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 2)
})

Promise.race([p1, p2]).then((value) => {
  console.log(value) // 2
})

Promise.race([p1, p2, 3]).then((value) => {
  console.log(value) // 3
})

Source code implementation

Smart, you must know how to implement it right away. As long as you understand which instance changed first, then Promise.race will follow this result, then you can write the following code

Promise.myRace = (promises) => {
  return new Promise((rs, rj) => {
    promises.forEach((p) => {
      // 对p进行一次包装,防止非Promise对象
      // 并且对齐进行监听,将我们自己返回的Promise的resolve,reject传递给p,哪个先改变状态,我们返回的Promise也将会是什么状态
      Promise.resolve(p).then(rs).catch(rj)
    })
  })
}

// 测试一下
const p1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 1)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 2)
})

Promise.myRace([p1, p2]).then((value) => {
  console.log(value) // 2
})

Promise.myRace([p1, p2, 3]).then((value) => {
  console.log(value) // 3
})

end

Maybe you and I have never met, but it's likely that we will meet each other late. I hope that here can become your habitat, and I would like to reap the joy with you and go for growth.

The above is the analysis of the first handwritten realization principle! Welcome everyone to correct possible errors and problems


前端胖头鱼
3.7k 声望6.2k 粉丝