前端面试复习3---异步与 Promise

127 阅读17分钟

前端面试复习 3---异步与 Promise

计算机原理:进程与线程

  • 进程:CPU 资源分配的最小单位
  • 线程:CPU 调度的最小单位
  • 解读:每一辆车代表我们日常用的每个程序,洗车场代表电脑的内存空间。每个停车位就代表了每个进程,小轿车占用小车位,大巴车占用大车位,等同于每个应用程序也会根基自身的需要的运行内存,分配不一样的进程空间。每个洗车工人代表一个线程,一个洗车位可以用多个洗车工人在干活,例如:洗车外面的,清理内饰的同时在进行;等同于一个应用程序的进程空间可以多个线程在同时干活,例如:网易云音乐,在播发音乐的同时,你还可以发表评论。同一个洗车位内的工人可以共用这个洗车位内的所以工具资源,同一个进程内的线程共用这个进程内的资源空间。一个车位至少一位洗车工,一个进程至少一个线程。

20220604_112319__.png

面试题:浏览器新开一个窗口,是进程还是线程?

  1. 答案:进程,从区别概念回答
  2. 发散 1:窗口(进程间)通信怎么通讯?storage、session、cookie => storage、cookie 的区别? => 怎么样操作 cookie,前端操作 cookie 有什么风险?=> cookie 的应用场景?
  3. 发散 2:浏览器原理?

浏览器原理-网页

  1. GUI 渲染线程:
    1. 解析 HTML CSS 构建 DOM 树->布局->绘制
    2. 与 JS 引擎线程互斥,当执行 JS 引擎线程时,GUI 渲染会被挂起,当任务队列空闲时,主线程才回去执行 GUI
  2. JS 引擎线程:
    1. 处理 JS,解析执行脚本
    2. 分配、处理、执行了待执行脚本同时,处理待执行事件,维护事件队列
    3. 阻塞 GUI 渲染。js 为何会阻塞 GUI 渲染:因为网页是单线程的,js 执行的时候,渲染会被挂起。
  3. 定时器触发线程:
    1. 异步定时器的处理和执行 - setTimeout / setInterval
    2. 接收 JS 引擎分配的定时器任务,并执行
    3. 处理完成后交于事件触发线程
  4. 异步 HTTP 请求线程:
    1. 异步执行请求类操作
    2. 接收 JS 引起线程异步请求操作
    3. 监听回调,交给事件触发线程做处理
  5. 事件触发线程:
    1. 接收所有来源的事件
    2. 将回调的事件依次加入到任务队列的队尾,交给 JS 引擎执行

event loop

事件循环(Event Loop)是 JavaScript 的执行机制之一,用于管理和调度异步任务的执行顺序。

事件循环的核心思想是基于一个事件队列(Event Queue)和一个调用栈(Call Stack)来实现。当 JavaScript 引擎执行代码时,同步任务会被直接放入调用栈中执行,而异步任务则会被放入事件队列中等待执行。

事件队列中的任务分为宏任务(macrotask)和微任务(microtask)两种类型。

当调用栈为空时,事件循环开始执行。首先,事件循环会将队列中的微任务一次性地全部取出并执行完毕,然后检查浏览器是否需要进行页面渲染。接下来,事件循环会从宏任务队列中取出一个任务,并放入调用栈中执行。当宏任务执行完毕后,再次执行微任务,然后重新渲染页面(如果需要),继续循环这个过程。

通过事件循环的机制,JavaScript 可以实现非阻塞的异步编程,使得程序能够同时处理多个任务,并提高了程序的响应性和性能。同时,了解事件循环的原理也有助于我们更好地理解 JavaScript 的执行过程,并编写出更高效的异步代码。

微任务与宏任务

宏任务(macro-task)和微任务(micro-task)是指在 JavaScript 执行过程中,不同类型的任务分类。

宏任务通常包括以下几种类型:

  • 渲染事件(如页面加载、重新渲染)
  • 用户交互事件(如点击、滚动)
  • 定时器事件(如 setTimeout、setInterval)
  • 网络请求完成、文件读写完成等 I/O 操作的回调
  • 执行整体的 JavaScript 代码(同步代码)

而微任务则包括以下几种类型:

  • Promise 的 resolve 回调函数
  • MutationObserver 的回调函数

在事件循环中,当一个宏任务执行完毕后,事件循环会检查微任务队列,并将其中的所有微任务连续执行,直到微任务队列为空。这意味着,微任务会在当前宏任务执行结束后立即执行,而不会等待下一个宏任务。

具体来说,当浏览器遇到一个宏任务时,它会进入宏任务队列,并等待执行。而当浏览器遇到一个微任务时,它会立即执行,并将所有产生的新的微任务加入微任务队列中。

通过合理利用微任务,我们可以在宏任务的执行间隙执行一些高优先级的任务,从而提高页面的响应性和交互性。常见的应用场景是使用 Promise 进行异步操作,并在操作完成后立即执行相关的处理逻辑。

总结来说,宏任务表示较为宏观的、需要较长时间才能执行完毕的任务,而微任务则表示较小粒度、需要快速执行的任务。掌握宏任务和微任务的执行顺序和使用场景,可以帮助我们更好地处理异步代码和优化程序性能。

event loop 基本过程

  1. 初始化阶段:事件循环从全局上下文开始,并执行同步代码。任何遇到的异步任务将被放入相应的任务队列中。
  2. 宏任务阶段:执行异步任务队列中的微任务。
  3. 渲染阶段:如果 DOM 被改变,渲染 DOM。
  4. 宏任务阶段:在微任务阶段结束后,事件循环会从宏任务队列中选择一个任务。这个任务将被推送到调用栈中执行,直到任务执行完毕。
  5. 重复循环:重复进行微任务阶段、宏任务阶段和渲染阶段,直到没有更多的任务。

这就是事件循环的基本过程,它通过不断地处理微任务和宏任务,以及渲染阶段的操作,使得 JavaScript 能够处理异步任务并保持页面的响应性。注意,这只是一个简化的描述,实际的事件循环机制可能会有更多的细节和特殊情况。

graph LR

A[同步代码]
A --> |存放| B[微任务队列]
A --> |存放| C[宏任务队列]
  B --> |拥有| B1[微任务 1]
  B --> |拥有| B2[微任务 2]
  B --> |拥有| B3[微任务..]
  C --> |拥有| C1[宏任务 1]
  C --> |拥有| C2[宏任务2]
  C --> |拥有| C3[宏任务..]
  D[微任务队列被全部执行]
    B1 --> |执行| D
    B2 --> |执行| D
    B3 --> |执行| D
      D --> E[DOM如果被改变就重新渲染]
      E --> |所有微任务执行完后执行| C1 --> |event loop| C2 --> |event loop| C3

上面整个流程图就是 event loop 的流程,在执行宏任务 1时会继续产生一个这样的 event loop 流程,直至宏任务 1被执行完,然后执行宏任务 2再产生这样的 event loop 流程,直到所有宏任务执行结束

执行栈/调用栈示例 demo

function fn2() {
  throw new Error('抛出一个错误')
}
function fn1() {
  fn2()
}
function run() {
  fn1()
  console.log('fn2抛出错误后,这里已经不执行了')
}
run()

// 执行栈后进先出
// 执行栈:run->fn1->fn2
// 执行完成顺序:fn2->fn1->run
// 先执行完成fn2再往下执行fn1,执行完成fn1再往下执行run,因为fn2报错了,所以fn1和run都无法往下执行完成

面试题 1:死循环

js 堆栈执行顺序与堆栈溢出/ 爆栈 / 性能卡顿 / => js 性能优化

死循环例子:

function fn() {
  fn()
}
fn()

正确情况下不会写出这样的死循环,如果在 vue 的 computed 中, 对某个和 computed 有关系的的变量进行复制就会造成这样的死循环。 例如:

{
  data() {
    return {
      bbb: 222,
      ccc: 333,
    }
  },
  computed: {
    aaa() {
      this.bbb = 111
      return this.bbb + this.ccc
    }
  }
}

面试题 2:执行顺序

setTimeout(() => {
  console.log('setTimeout') // 5. 宏任务2
})

new Promise((resolve, reject) => {
  console.log('new Promise') // 1. 属于同步进入主线程 宏任务1
  resolve()
})
  .then(() => {
    console.log('Promise then') // 3. 微任务1
  })
  .then(() => {
    console.log('Promise then then') // 4. 微任务2
  })

console.log('hi') // 2. 同步代码 宏任务1

解题思路:任务维度

promise 有哪些状态?

  1. pending/待处理的, fulfilled/满足的, rejected/被驳回的
  2. executor: new Promise() 的时候立即执行,接收两个参数 resolve, reject

promise 的默认状态是什么?状态是如何流转的?

  1. 默认:pending
  2. 状态流转:pending => fulfilled | rejected

手写 Promise

Promise 规范

  1. Promise 实例有三个状态: pending fulfilled rejected,默认状态为 pending
  2. 状态只能从 pending 向 fulfilled | rejected 流转
  3. Promise(executor) 接收一个参数 executor
  4. executor(resolve, reject) 是同步执行的,同时接收两个两个参数 resolve reject
  5. resolve(value) 成功的回调,接收一个任何类型的参数作为成功的返回值,执行 resolve(value) 会把实例的状态变为 fulfilled
  6. reject(reason) 失败的回调,接收一个参数通常是字符串作为失败的原因,执行 reject(reason) 会把实例的状态变为 rejected
  7. 实例内部需要有一个 result 属性,用来存储 resolve(value) reject(reason) 传进来的的 value 或 reason
  8. Promise 的 then(onResolved, onRejected) 方法接收两个参数 onResolved, onRejected,目的是为了可以拿到内部的 result
  9. 当状态为 fulfilled 时执行 onResolved,result 为 resolve(value) 传进来的 value
  10. 当状态为 rejected 时执行 onRejected,result 为 reject(reason) 传进来的 reason
  11. then 返回的是一个 Promise 实例,这个实例的状态是由 onResolved onRejected 的返回结果决定的,onResolved onRejected 的返回结果不是一个 Promise 实例时,then 方法都会返回一个状态为 fulfilled 的 Promise 实例, onResolved onRejected 的返回结果是一个 Promise 实例时,这个 实例的状态就是 then 方法返回实例的状态
  12. Promise 的 catch(onRejected) 方法接收一个参数 onRejected
  13. catch 返回的同样是一个 Promise 实例,这个实例的状态是由 onRejected 的返回结果决定的

1. 基本框架

class Promise {
  // 用静态属性定义三个状态,方便后面使用
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  constructor(executor) {
    this.result = undefined // 存储 resolve(value) reject(reason) 传进来的的 value 或 reason
    this.state = Promise.PENDING // 默认状态为 pending

    const resolve = (value) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.FULFILLED
        this.result = value
      }
    }

    const reject = (reason) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.REJECTED
        this.result = reason
      }
    }

    // 这里用 try catch 是为了解决像 p3 的情况,在 executor 实参的函数体中有报错
    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }
}

const p1 = new Promise((resolve, reject) => {
  resolve('test')
})
console.log(p1)

const p2 = new Promise((resolve, reject) => {
  reject('test')
})
console.log(p2)

const p3 = new Promise((resolve, reject) => {
  throw 'test'
})
console.log(p3)

2. 添加 then 方法

class Promise {
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  constructor(executor) {
    this.result = undefined
    this.state = Promise.PENDING

    const resolve = (value) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.FULFILLED
        this.result = value
      }
    }

    const reject = (reason) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.REJECTED
        this.result = reason
      }
    }

    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  // 添加 then 方法,参数 onResolved, onRejected 函数是为了拿到内部维护的 this.result
  then(onResolved, onRejected) {
    // onResolved, onRejected 必须是函数如果不是把它们转为函数
    if (typeof onResolved !== 'function') onResolved = (value) => value
    if (typeof onRejected !== 'function')
      onRejected = (reason) => {
        throw reason
      }

    if (this.state === Promise.FULFILLED) {
      onResolved(this.result) // 将内部的 this.result 传出去
    } else if (this.state === Promise.REJECTED) {
      onRejected(this.result)
    }
  }
}

/**
 * executor 中的 resolve('ok') 将内部的 this.result 维护成 'ok'
 * 再通过 then(onResolved) 的 onResolved 将 this.result 拿到
 */
const p1 = new Promise((resolve, reject) => {
  resolve('ok')
})
p1.then((value) => {
  console.log(value)
})

const p2 = new Promise((resolve, reject) => {
  reject('fail')
})
p2.then(null, (reason) => {
  console.log(reason)
})

3. then 方法添加异步的情况

class Promise {
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  constructor(executor) {
    this.result = undefined
    this.state = Promise.PENDING
    // 1. 存 then(onResolved, onRejected) 传进来的两个参数函数
    this.callback = {
      onResolved: null,
      onRejected: null
    }

    const resolve = (value) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.FULFILLED
        this.result = value
        if (this.callback.onResolved) {
          // 3. 执行在 then 方法存进来的 onResolved
          this.callback.onResolved(value)
        }
      }
    }

    const reject = (reason) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.REJECTED
        this.result = reason
        if (this.callback.onRejected) {
          this.callback.onRejected(reason)
        }
      }
    }

    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onResolved, onRejected) {
    if (typeof onResolved !== 'function') onResolved = (value) => value
    if (typeof onRejected !== 'function')
      onRejected = (reason) => {
        throw reason
      }

    if (this.state === Promise.FULFILLED) {
      onResolved(this.result)
    } else if (this.state === Promise.REJECTED) {
      onRejected(this.result)
    } else if (this.state === Promise.PENDING) {
      // 2. resolve reject 在 setTimeout 里面是异步代码,执行 then 的时候它们俩还没执行,实例的状态还是 pending
      // 这种情况现将 onResolved onRejected 存起来交到 resolve reject 再执行
      this.callback = { onResolved, onRejected }
    }
  }
}

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('resolve in setTimeout')
  }, 1000)
})
p1.then((value) => {
  console.log(value)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('reject in setTimeout')
  }, 2000)
})
p2.then(null, (reason) => {
  console.log(reason)
})

4. 实例多次调用 then 的情况

class Promise {
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  constructor(executor) {
    this.result = undefined
    this.state = Promise.PENDING
    // 1. this.callbacks 改成数组,可以存多个 { onResolved, onRejected } 这样的对象
    this.callbacks = []

    const resolve = (value) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.FULFILLED
        this.result = value
        // 3. 遍历执行在 then 方法存进来的 onResolved
        this.callbacks.forEach((item) => {
          item.onResolved(value)
        })
      }
    }

    const reject = (reason) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.REJECTED
        this.result = reason
        this.callbacks.forEach((item) => {
          item.onRejected(reason)
        })
      }
    }

    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onResolved, onRejected) {
    if (typeof onResolved !== 'function') onResolved = (value) => value
    if (typeof onRejected !== 'function')
      onRejected = (reason) => {
        throw reason
      }

    if (this.state === Promise.FULFILLED) {
      onResolved(this.result)
    } else if (this.state === Promise.REJECTED) {
      onRejected(this.result)
    } else if (this.state === Promise.PENDING) {
      // 2. 执行 then 的时候,如果状态还是 pending,
      // onResolved, onRejected 先通过 this.callbacks 存起来,交到 resolve reject 再执行
      this.callbacks.push({ onResolved, onRejected })
    }
  }
}

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('resolve in setTimeout')
  }, 1000)
})
p1.then((value) => {
  console.log('1 ' + value)
})
p1.then((value) => {
  console.log('2 ' + value)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('reject in setTimeout')
  }, 1000)
})
p2.then(null, (reason) => {
  console.log('1 ' + reason)
})
p2.then(null, (reason) => {
  console.log('2 ' + reason)
})

5. then 方法返回一个新的 Promise 实例

class Promise {
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  constructor(executor) {
    this.result = undefined
    this.state = Promise.PENDING
    this.callbacks = []

    const resolve = (value) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.FULFILLED
        this.result = value
        this.callbacks.forEach((item) => {
          item.onResolved(value)
        })
      }
    }

    const reject = (reason) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.REJECTED
        this.result = reason
        this.callbacks.forEach((item) => {
          item.onRejected(reason)
        })
      }
    }

    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  /**
   * 原本的思路不变, 还是通过 onResolved, onRejected 将 this.result 传出去,
   * 但是这里同时要拿到 onResolved, onRejected 的返回结果,
   * 根据这个结果,然后通过被返回的实例的 resolve 和 reject 改变这个被返回的实例的状态和返回的值
   */
  then(onResolved, onRejected) {
    if (typeof onResolved !== 'function') onResolved = (value) => value
    if (typeof onRejected !== 'function')
      onRejected = (reason) => {
        throw reason
      }

    return new Promise((resolve, reject) => {
      if (this.state === Promise.FULFILLED) {
        try {
          const res = onResolved(this.result)
          if (res instanceof Promise) {
            // onResolved 的返回结果是一个 Promise 实例时,onResolved 的返回实例的状态就是 then 方法返回实例的状态
            res.then(
              (v) => {
                resolve(v)
              },
              (r) => {
                reject(r)
              }
            )
          } else {
            // onResolved 的返回结果不是一个 Promise 实例时,then 方法都会返回一个状态为 fulfilled 的 Promise 实例
            resolve(res)
          }
        } catch (error) {
          // onResolved 中有报错的时候会进入这里
          reject(error)
        }
      } else if (this.state === Promise.REJECTED) {
        onRejected(this.result)
      } else if (this.state === Promise.PENDING) {
        this.callbacks.push({ onResolved, onRejected })
      }
    })
  }
}

const p1 = new Promise((resolve) => {
  resolve('onResolved 没有 return 即返回 undefined')
})
const p11 = p1.then((value) => {
  value
  // return value
})
console.log(p11)

const p2 = new Promise((resolve, reject) => {
  resolve('onResolved 返回一个状态为 fulfilled 的 promise')
  // reject('onRejected 返回一个状态为 rejected 的 promise')
})
const p22 = p2.then((value) => {
  return new Promise((resolve) => {
    resolve(value)
  })
})
console.log(p22)

const p3 = new Promise((resolve) => {
  resolve('onResolved 有报错')
})
const p33 = p3.then((value) => {
  return new Promise((resolve, reject) => {
    throw value
  })
})
console.log(p33)

6. then 方法返回一个新的 Promise 实例时也考虑异步的情况

class Promise {
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  constructor(executor) {
    this.result = undefined
    this.state = Promise.PENDING
    this.callbacks = []

    const resolve = (value) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.FULFILLED
        this.result = value
        this.callbacks.forEach((item) => {
          item.onResolved(value)
        })
      }
    }

    const reject = (reason) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.REJECTED
        this.result = reason
        this.callbacks.forEach((item) => {
          item.onRejected(reason)
        })
      }
    }

    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onResolved, onRejected) {
    if (typeof onResolved !== 'function') onResolved = (value) => value
    if (typeof onRejected !== 'function')
      onRejected = (reason) => {
        throw reason
      }

    return new Promise((resolve, reject) => {
      if (this.state === Promise.FULFILLED) {
        try {
          const res = onResolved(this.result)
          if (res instanceof Promise) {
            res.then(
              (v) => {
                resolve(v)
              },
              (r) => {
                reject(r)
              }
            )
          } else {
            resolve(res)
          }
        } catch (error) {
          reject(error)
        }
      } else if (this.state === Promise.REJECTED) {
        onRejected(this.result)
      } else if (this.state === Promise.PENDING) {
        /**
         * 和之前一样的是:遇到 pending 的时候先把 onResolved 通过 callbacks 存起来
         * 不一样的是:之前只要执行 onResolved 把值传出去,现在要多执行 resolve 改变被返回 Promise 实例的状态
         */
        this.callbacks.push({
          onResolved: () => {
            // const res = onResolved(this.result)
            // resolve(res)
            // 如果不考虑 onResolved 返回 Promise 和 执行中报错的情况只要上面两句即可
            try {
              const res = onResolved(this.result)
              if (res instanceof Promise) {
                res.then(
                  (v) => {
                    resolve(v)
                  },
                  (r) => {
                    reject(r)
                  }
                )
              } else {
                resolve(res)
              }
            } catch (error) {
              reject(error)
            }
          },
          onRejected: () => {
            try {
              const res = onRejected(this.result)
              if (res instanceof Promise) {
                res.then(
                  (v) => {
                    resolve(v)
                  },
                  (r) => {
                    reject(r)
                  }
                )
              } else {
                resolve(res)
              }
            } catch (error) {
              reject(error)
            }
          }
        })
      }
    })
  }
}

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve('resolve in setTimeout')
    reject('reject in setTimeout')
  }, 1000)
})

const p11 = p1.then(
  (value) => {
    return value
    // throw value
  },
  (reason) => {
    return reason
  }
)
console.log(p11)

7. then 方法,代码优化

class Promise {
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  constructor(executor) {
    this.result = undefined
    this.state = Promise.PENDING
    this.callbacks = []

    const resolve = (value) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.FULFILLED
        this.result = value
        this.callbacks.forEach((item) => {
          item.onResolved(value)
        })
      }
    }

    const reject = (reason) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.REJECTED
        this.result = reason
        this.callbacks.forEach((item) => {
          item.onRejected(reason)
        })
      }
    }

    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onResolved, onRejected) {
    if (typeof onResolved !== 'function') onResolved = (value) => value
    if (typeof onRejected !== 'function')
      onRejected = (reason) => {
        throw reason
      }

    return new Promise((resolve, reject) => {
      /**
       * 回调逻辑抽离
       * @param {onResolved, onRejected } type
       */
      const callback = (type) => {
        try {
          const res = type(this.result)
          if (res instanceof Promise) {
            res.then(
              (v) => {
                resolve(v)
              },
              (r) => {
                reject(r)
              }
            )
          } else {
            resolve(res)
          }
        } catch (error) {
          reject(error)
        }
      }

      if (this.state === Promise.FULFILLED) {
        callback(onResolved)
      } else if (this.state === Promise.REJECTED) {
        callback(onRejected)
      } else if (this.state === Promise.PENDING) {
        this.callbacks.push({
          onResolved: () => {
            callback(onResolved)
          },
          onRejected: () => {
            callback(onRejected)
          }
        })
      }
    })
  }
}

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve('resolve in setTimeout')
    reject('reject in setTimeout')
  }, 1000)
}).then(
  (value) => {
    return value
    // throw value
  },
  (reason) => {
    return reason
  }
)
console.log(p1)

const p2 = new Promise((resolve, reject) => {
  resolve('ok')
  // reject('fail')
}).then(
  (value) => {
    return value
    // throw value
  },
  (reason) => {
    return reason
  }
)
console.log(p2)

8. 类方法 resolve reject

class Promise {
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  constructor(executor) {
    this.result = undefined
    this.state = Promise.PENDING
    this.callbacks = []

    const resolve = (value) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.FULFILLED
        this.result = value
        this.callbacks.forEach((item) => {
          item.onResolved(value)
        })
      }
    }

    const reject = (reason) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.REJECTED
        this.result = reason
        this.callbacks.forEach((item) => {
          item.onRejected(reason)
        })
      }
    }

    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onResolved, onRejected) {
    if (typeof onResolved !== 'function') onResolved = (value) => value
    if (typeof onRejected !== 'function')
      onRejected = (reason) => {
        throw reason
      }

    return new Promise((resolve, reject) => {
      const callback = (type) => {
        try {
          const res = type(this.result)
          if (res instanceof Promise) {
            res.then(
              (v) => {
                resolve(v)
              },
              (r) => {
                reject(r)
              }
            )
          } else {
            resolve(res)
          }
        } catch (error) {
          reject(error)
        }
      }

      if (this.state === Promise.FULFILLED) {
        callback(onResolved)
      } else if (this.state === Promise.REJECTED) {
        callback(onRejected)
      } else if (this.state === Promise.PENDING) {
        this.callbacks.push({
          onResolved: () => {
            callback(onResolved)
          },
          onRejected: () => {
            callback(onRejected)
          }
        })
      }
    })
  }

  catch(onRejected) {
    return this.then(null, onRejected)
  }

  // 添加类方法 resolve,这里换了个思路: 如果入参是 promise 直接返回去
  static resolve(value) {
    if (value instanceof Promise) {
      return value
    } else {
      return new Promise((resolve, reject) => {
        resolve(value)
      })
    }
  }

  // 旧思路,添加类方法 resolve
  static resolve1(value) {
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) {
        value.then(
          (v) => {
            resolve(v)
          },
          (r) => {
            reject(r)
          }
        )
      } else {
        resolve(value)
      }
    })
  }

  // 添加类方法 reject
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }
}

const p1 = Promise.resolve('ok')
console.log(p1)

const p2 = Promise.resolve(
  new Promise((resolve, reject) => {
    // resolve('ok')
    reject('err')
  })
)
console.log(p2)

const p3 = Promise.reject('error')
console.log(p3)

const p4 = Promise.reject(
  new Promise((resolve, reject) => {
    resolve('ok-p4')
  })
)
console.log(p4)

9. 类方法 all race

class Promise {
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  constructor(executor) {
    this.result = undefined
    this.state = Promise.PENDING
    this.callbacks = []

    const resolve = (value) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.FULFILLED
        this.result = value
        this.callbacks.forEach((item) => {
          item.onResolved(value)
        })
      }
    }

    const reject = (reason) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.REJECTED
        this.result = reason
        this.callbacks.forEach((item) => {
          item.onRejected(reason)
        })
      }
    }

    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onResolved, onRejected) {
    if (typeof onResolved !== 'function') onResolved = (value) => value
    if (typeof onRejected !== 'function')
      onRejected = (reason) => {
        throw reason
      }

    return new Promise((resolve, reject) => {
      const callback = (type) => {
        try {
          const res = type(this.result)
          if (res instanceof Promise) {
            res.then(
              (v) => {
                resolve(v)
              },
              (r) => {
                reject(r)
              }
            )
          } else {
            resolve(res)
          }
        } catch (error) {
          reject(error)
        }
      }

      if (this.state === Promise.FULFILLED) {
        callback(onResolved)
      } else if (this.state === Promise.REJECTED) {
        callback(onRejected)
      } else if (this.state === Promise.PENDING) {
        this.callbacks.push({
          onResolved: () => {
            callback(onResolved)
          },
          onRejected: () => {
            callback(onRejected)
          }
        })
      }
    })
  }

  catch(onRejected) {
    return this.then(null, onRejected)
  }

  static resolve(value) {
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) {
        value.then(
          (v) => {
            resolve(v)
          },
          (r) => {
            reject(r)
          }
        )
      } else {
        resolve(value)
      }
    })
  }

  // 添加类方法 reject
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }

  // 添加类方法 all
  static all(promises) {
    return new Promise((resolve, reject) => {
      let count = 0
      const list = []
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          (v) => {
            count++
            list[i] = v
            if (count === promises.length) {
              resolve(list)
            }
          },
          (r) => {
            reject(r)
          }
        )
      }
    })
  }

  // 添加类方法 race
  static race(promises) {
    return new Promise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          (v) => {
            resolve(v)
          },
          (r) => {
            reject(r)
          }
        )
      }
    })
  }
}

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok1')
  })
})
const p2 = Promise.resolve('ok2')
// const p3 = Promise.reject('error')

const all = Promise.all([p1, p2])
console.log(all)

const race = Promise.race([p1, p2])
console.log(race)

10. 完结 + 优化细节 then(onResolve, onRejected) 中的 onResolve, onRejected 是异步执行的

class Promise {
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  constructor(executor) {
    this.result = undefined
    this.state = Promise.PENDING
    this.callbacks = []

    const resolve = (value) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.FULFILLED
        this.result = value
        // 添加异步执行
        setTimeout(() => {
          this.callbacks.forEach((item) => {
            item.onResolved(value)
          })
        })
      }
    }

    const reject = (reason) => {
      if (this.state === Promise.PENDING) {
        this.state = Promise.REJECTED
        this.result = reason
        // 添加异步执行
        setTimeout(() => {
          this.callbacks.forEach((item) => {
            item.onRejected(reason)
          })
        })
      }
    }

    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onResolved, onRejected) {
    if (typeof onResolved !== 'function') onResolved = (value) => value
    if (typeof onRejected !== 'function')
      onRejected = (reason) => {
        throw reason
      }

    return new Promise((resolve, reject) => {
      const callback = (type) => {
        try {
          const res = type(this.result)
          if (res instanceof Promise) {
            res.then(
              (v) => {
                resolve(v)
              },
              (r) => {
                reject(r)
              }
            )
          } else {
            resolve(res)
          }
        } catch (error) {
          reject(error)
        }
      }

      if (this.state === Promise.FULFILLED) {
        // 添加异步执行
        setTimeout(() => {
          callback(onResolved)
        })
      } else if (this.state === Promise.REJECTED) {
        // 添加异步执行
        setTimeout(() => {
          callback(onRejected)
        })
      } else if (this.state === Promise.PENDING) {
        this.callbacks.push({
          onResolved: () => {
            callback(onResolved)
          },
          onRejected: () => {
            callback(onRejected)
          }
        })
      }
    })
  }

  catch(onRejected) {
    return this.then(null, onRejected)
  }

  static resolve(value) {
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) {
        value.then(
          (v) => {
            resolve(v)
          },
          (r) => {
            reject(r)
          }
        )
      } else {
        resolve(value)
      }
    })
  }

  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }

  static all(promises) {
    return new Promise((resolve, reject) => {
      let count = 0
      const list = []
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          (v) => {
            count++
            list[i] = v
            if (count === promises.length) {
              resolve(list)
            }
          },
          (r) => {
            reject(r)
          }
        )
      }
    })
  }

  static race(promises) {
    return new Promise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          (v) => {
            resolve(v)
          },
          (r) => {
            reject(r)
          }
        )
      }
    })
  }
}

const p1 = new Promise((resolve, reject) => {
  resolve('ok1')
  // reject('ok1')
  console.log(111)
})

p1.then(
  (v) => {
    console.log('222-1')
  },
  (r) => {
    console.log('222-2')
  }
)
console.log(333)

async await & generator

async await

function p() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('延时1秒执行')
    }, 1000)
  })
}

async function asyncCall() {
  const result = await p()
  console.log('asyncCall---', result)
}
asyncCall()

generator 函数

  1. generator 就是生成器函数。是协程在 es6 中的一个实现。它最大的特征就是可以交出函数的执行权。
  2. generator 函数的执行和传统的不一样,它的返回值是一个迭代器对象。
  3. 简单理解就是返回值是一个对象,有两个属性,一个是 value,一个是 donevlaue就是返回的值,done布尔类型,false代表还没完成迭代,true代表完成了迭代。
  4. 遇到 yield就会暂停,执行 next()就会继续执行并返回一个迭代器对象。

PS: 协成

  1. 协程是一种基于线程之上,但又比线程更加轻量级的存在。对内核来说具有不可见性。
  2. 一个进程可以有多个线程。一个线程可以有多个协程。

简单使用

function* fn() {
  yield 1
  yield 2
  yield 3
}

const gen = fn()

console.log(gen.next()) // {value: 1, done: false}
console.log(gen.next()) // {value: 2, done: false}
console.log(gen.next()) // {value: 3, done: false}
console.log(gen.next()) // {value: undefined, done: true}

结合 promise 请求

function* generatorFn() {
  const run1 = yield request(1)
  console.log('run1---', run1) // 执行第二next()的时候这里才会执行, run1 等于第二个 next() 传进来的参数

  const run2 = yield request(2)
  console.log('run2---', run2)

  const run3 = yield request(3)
  console.log('run3---', run3)

  const run4 = yield request(4)
  console.log('run4---', run4)

  const run5 = yield 'ok'
  console.log('run5---', run5)
}

// generatorFn 里面虽然有多个异步请求,但是我们可以看到代码样式是同步的

function run(generator) {
  const iterator = generator()
  function next(res) {
    let { value, done } = iterator.next(res)
    if (!(value instanceof Promise)) {
      console.log(`value是${value},不是promise对象`)
      value = Promise.resolve(value)
    }
    if (done) {
      console.log('done---', done)
      return
    }
    value.then(next)
  }
  next()
}

run(generatorFn)

总结

  1. 其实 generator 在我们实际开发中并不常见,因为它当时主要用于 js 的异步解决方案,但是后来被更好的 async await 替代了,async await 也是 js 异步的终极解决方案。
  2. 但是 generator 独特的特性可以让我们在函数执行的过程中传递参数获取结果,使得函数调用变得更加灵活。