前端基础打卡3-promise规范与异步控制流(手写promise)

97 阅读2分钟

记录一下前端学习笔记,每天打卡!! 目标:

必知必会: js | 布局样式 | 客户端 | 技术栈

加分项:工具(CI/CD等) 热门模块工程化

进阶:模式 实战/算法

开始第三天知识学习

promise

promise解决了回调地狱

手写promise

enum PromiseStatus {
  PENDING = 'pending',
  FULFILLED = 'fulfilled',
  REJECT = 'reject'
}

class MyPromise<T = any>{
  status = PromiseStatus.PENDING

  value: T
  reason: any

  // then回调函数的队列
  onFulfilledFns = new Array<(value: T) => void>()
  onRejectedFns = new Array<(reason: any) => void>()

  constructor(executor: (
    resolve: (value: T) => void,
    reject: (value: T) => void) => void) {

    const resolve = (value: T) => {
      if (this.status === PromiseStatus.PENDING) {
        queueMicrotask(() => {
          if (this.status !== PromiseStatus.PENDING) return
          this.status = PromiseStatus.FULFILLED
          this.value = value

          this.onFulfilledFns.forEach(fn => fn(this.value))
        })
      }
    }

    const reject = (reason: any) => {
      if (this.status === PromiseStatus.PENDING) {
        queueMicrotask(() => {
          if (this.status !== PromiseStatus.PENDING) return
          this.status = PromiseStatus.REJECT
          this.reason = reason

          this.onRejectedFns.forEach(fn => fn(this.value))
        })
      }
    }

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

  then(
    onFulfilled: (value: T) => any = (value) => value,
    onRejected: (reason: any) => any = (reason) => reason) {
    return new MyPromise((resolve, reject) => {
      // 如果当前Promise状态为已知
      if (this.status === PromiseStatus.FULFILLED) {
        try {
          const res = onFulfilled(this.value)
          resolve(res)
        } catch (error) {
          reject(error)
        }
      }
      if (this.status === PromiseStatus.REJECT) {
        try {
          const res = onRejected(this.reason)
          resolve(res)
        } catch (error) {
          reject(error)
        }
      }

      // 如果当前Promise状态未知
      if (this.status === PromiseStatus.PENDING) {
        this.onFulfilledFns.push(() => {
          try {
            const res = onFulfilled(this.value)
            resolve(res)
          } catch (error) {
            reject(error)
          }
        })
        this.onRejectedFns.push(() => {
          try {
            const res = onRejected(this.reason)
            resolve(res)
          } catch (error) {
            reject(error)
          }
        })
      }
    })
  }

  catch(onRejected: (reason: any) => any = (reason) => reason) {
    return this.then(undefined, onRejected)
  }

  finally(onFinally: () => void) {
    return this.then(() => onFinally, () => onFinally)
  }

  static resolve<T>(value: T) {
    return new MyPromise<T>(resolve => resolve(value))
  }

  static reject(reason: any) {
    return new MyPromise((resolve, reject) => reject(reason))
  }

  static all(promises: Array<MyPromise>): MyPromise<any[]> {
    return new MyPromise((resolve, reject) => {
      const values: any[] = []
      let finishCount = 0
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(res => {
          values[i] = res
          finishCount++
          if (finishCount == promises.length) {
            resolve(values)
          }
        }), err => {
          reject(err)
        }
      }
    })
  }
}


const p1 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    console.log("--- 1 ---");
    resolve(111);
  });
}).then(res => {
  console.log("p1 res :>> ", res);
});

const p2 = new MyPromise((resolve, reject) => {
  console.log("--- 2 ---");
  resolve(222);
});

const p3 = new MyPromise((resolve, reject) => {
  console.log("--- 3 ---");
  resolve(333);
});

思考:有没有像写同步的代码,来写异步呢?

Generator函数

import fsp from 'fs/promises';

function* func() {
  const res1 = yield fsp.readFile("test1.txt")
  console.log(res1.toString())
  const res2 = yield fsp.readFile("text2.txt")
  console.log(res2.toString());
  const res3 = yield fsp.readFile("text3.txt")
  console.log(res3.toString())
  return
}


const g = func()

// g.next()
g.next().value.then(data => {
  g.next(data).value.then(data => {
    g.next(data).value.then(data => {
      g.next(data)
    })
  })
})

如上所述,虽然读取文件的异步操作写成了同步的方式,但是下面调用的方式还是回调的方式,有咩有更好的方法呢? 可以改成递归的方式。

function run(gen: () => Generator<Promise<Buffer>, void, unknown>) {
  const g = gen()

  function next(data?: any) {
    const result = g.next(data)
    if (result.done) return
    result.value.then(data => next(data))
  }

  next()
}

run(func)

这样就是解决异步的比较好的方法,其实后面ES6的async和await就是Generator和yield的语法糖,所以上面可以写成如下:

import fsp from 'fs/promises';

async function func() {
  const res1 = await fsp.readFile("test1.txt")
  console.log(res1.toString())
  const res2 = await fsp.readFile("text2.txt")
  console.log(res2.toString());
  const res3 = await fsp.readFile("text3.txt")
  console.log(res3.toString())
  return
}


func()
.then(res => {
  console.log(res)
})
.catch(err => {
  console.log(err)
})

由上,可知道async和await不仅是Generator和yield的语法糖,同时还对错误异常进行了处理,是比较好的异步处理方式。