面试必考 - 手写 Promise, 由浅入深(附源码)

前言

Hello 你们好!我是壹甲壹!前端

相信你们不管在前端仍是后端开发工做中,都接触并使用过 Promise ,本文将带领你们「step-by-step」实现一个符合 Promises/A+ 规范的 Promise,同时探索 Promise 中的一些方法以及第三方扩展如何实现的。node

经过阅读本篇文章你能够学习到:git

  • 手写实现符合规范的 Promise
  • 使用 promises-aplus-tests 进行规范测试
  • 掌握 Promise.allPromise.race, Promise.resolve, Promise.reject 等实现原理
  • 掌握 Node 中对 Promise 的一些扩展

在正式进入正题以前,为了更好地理解和掌握 Promise ,咱们先来介绍一些与 Promise 相关的基础知识。github

1、什么是异步

1.1 JS 中为何存在异步

你们应该都知道,JS 属于单线程语言,所谓单线程,就是一次只能干一件事,其它事情只能在后面乖乖排队等待。面试

在浏览器中,页面加载过程当中存在大量请求,当一个网络请求迟迟没有响应,页面将傻傻等着,不能处理其它事情。npm

所以,JS 中设计了异步,即发送完网络请求后就能够继续处理其它操做,而网络请求返回的数据,可经过回调函数来接收处理,这样就保证了页面的正常运行。json

1.2 异步解决方案

先看下面一段 Node 代码后端

var fs = require('fs')
fs.readFile('data.json', (err, data) => {  console.log(data.toString()) }) 复制代码

fs.readFile 方法的第二个参数是个函数,函数并不会当即执行,而是等到读取的文件结果出来才执行,这是函数就是回调函数,即 callbackapi

1.3 回调地狱

处理多个异步请求,而且一个一个嵌套时,就容易产生回调地狱。看下面一段 Node 代码数组

const fs = require('fs')
fs.readFile('data1.json', (err, data1) => {  fs.readFile('data2.json', (err, data2) => {  fs.readFile('data3.json', (err, data3) => {  fs.readFile('data4.json', (err, data4) => {  console.log(data4.toString())  })  })  }) }) 复制代码

使用 Promise 改写

const fs = require('fs')
const readFilePromise = (file) => {  return new Promise((resolve, reject) => {  fs.readFile(file, (err, data) => {  if (err) {  reject(err)  }  resolve(data)  })  }) } readFilePromise('data1.json') .then(data1 => {  return readFilePromise('data2.json') }).then(data2 => {  return readFilePromise('data3.json') }).then(data3 => {  return readFilePromise('data4.json') }).then(data4 => {  console.log(data4.toString()) }).catch(err => {  console.log(err) }) 复制代码

「思考题」:Promise 真的取代 callback 了嘛?

Promise 只是对于异步操做代码的可读性的一种变化,没有改变 JS 中异步执行的本质,也没法取代 callback 在 JS 中的存在。同时,在 Promise 中,也存在着 callback 的使用,实例的 then() 的参数分别是执行成功、失败的函数,也就是 callback 回调函数。

2、Promise 的实现

本篇文章对应的项目地址: github.com/Yangjia23..…

2.1 基本实现

2.1.1 executor 执行器

首先,Promise 是个类,须要使用 new 来建立实例

  • new Promise((resolve, reject) => {}) 传入的参数是个函数,被称为 executor 执行器,默认会当即执行
  • executor 执行时会传入两个参数 resolve, reject ,分别是执行成功函数、执行失败函数
  • resolve, reject 两个执行函数不属于 Promise 类上的静态属性,也不是实例上的方法,而是一个普通函数
class Promise {
 constructor (executor) {  // 成功  const resolve = () => {}  // 失败  const reject = () => {}  // 当即执行  executor(resolve, reject)  } } 复制代码

2.1.2 三种状态

关于 Promise 状态

  • promise 有三种状态:等待 (pending)已成功 (fulfilled)已失败(rejected),默认状态为 pending

  • promise 的状态只能从 pending 转换成 fulfilledrejected 两种状态变化

了解promise状态更多内容,请查看Promises/A+规范: promise-states

以 readFilePromise 为例

  • 读取文件成功时,会调用resolve函数,传入读取的内容,表示执行成功,此时的状态应是 fulfilled 成功态
  • 读取文件失败,会调用 reject 函数,传入失败的缘由,表示执行失败,此时的状态应是 fulfilled 失败态
  • 读取的文件内容或失败的缘由须要保存,分别使用 valuereason 存储
const ENUM = {
 PENDING: 'pending',  FULFILLED: 'fulfilled',  REJECTED: 'rejected' } class Promise {  constructor (executor) {  this.status = ENUM.PENDING // 默认状态  this.value = undefined // 保存执行成功的值  this.reason = undefined // 保存执行失败的缘由  // 成功  const resolve = (value) => {  if (this.status === ENUM.PENDING) {  this.status = ENUM.FULFILLED  this.value = value  }  }  // 失败  const reject = (reason) => {  if (this.status === ENUM.PENDING) {  this.status = ENUM.REJECTED  this.reason = reason  }  }  // 当即执行  executor(resolve, reject)  } } 复制代码

2.1.3 异常捕获

因为 executor 执行器是由用户传入的,在执行过程当中可能出现错误,此时须要使用 try...catch... 进行异常捕获,当发生错误后,直接调用 reject 抛出错误

class Promise {
 constructor (executor) {  // ....  // 异常捕获  try{  // 当即执行  executor(resolve, reject)  } catch (e) {  reject(e)  }  } } 复制代码

2.1.4 实现 then 方法

调用 new Promise() 返回的实例上有个 then 方法,then 方法须要用户提供两个参数,分别是执行成功后对应的成功回调 onFulfilled 和执行失败后对应的失败回调 onRejected

  • 当状态变成 fulfilled,会调用 onFulfilled 方法,并传入成功的值 this.value
  • 当状态变成 rejected,会调用 onRejected 方法,并传入失败的缘由 this.reason
class Promise {
 constructor(executor) {  // ...  }  then(onFulfilled, onRejected) {  if (this.status == ENUM.FULFILLED) {  onFulfilled(this.value)  }  if (this.status == ENUM.REJECTED) {  onRejected(this.reason)  }  } } 复制代码

executor 中执行的是异步操做时,执行 then 方法时状态仍是 pending

异步操做例如 setTimeout属于宏任务,而 promise.then 属于微任务, 微任务先于宏任务执行,因此then方法执行时,promise的状态仍是 pending

同时实例promise能够屡次调用 then 方法,因此,须要将全部 then 方法中的回调函数搜集保存好,当异步操做完成后,再执行保存的回调函数(基于发布订阅模式

const promise = new Promise((resolve, reject) => {
 setTimeout(() => {}, 2000) })  promise.then(data => {//...}, err => {})  promise.then(data => {//...}, err => {}) 复制代码

因此,接下来须要实现的是

  • 建立两个队列 onResolvedCallbacksonRejectedCallbacks,分别存放 then 方法中对应的成功回调和失败回调
  • 当异步操做成功时,调用 resolve 函数时,执行 onResolvedCallbacks 队列中每一个成功回调
  • 当异步操做失败时,调用 reject 函数时,执行 onRejectedCallbacks 队列中每一个失败回调
class Promise {
 constructor(executor) {  this.status = ENUM.PENDING  this.value = undefined  this.reason = undefined  this.onResolvedCallbacks = [] // 成功队列  this.onRejectedCallbacks = [] // 失败队列  // 成功回调  const resolve = (value) => {  if (this.status === ENUM.PENDING) {  this.status = ENUM.FULFILLED  this.value = value  this.onResolvedCallbacks.forEach(cb => cb()) // 相对于发布  }  }  // 失败回调  const reject = (reason) => {  if (this.status === ENUM.PENDING) {  this.status = ENUM.REJECTED  this.reason = reason  this.onRejectedCallbacks.forEach(cb => cb())  }  }  // 当即执行  executor(resolve, reject)  }  then(onFulfilled, onRejected) {  // ...  if (this.status === ENUM.PENDING) {  // 相对于订阅  this.onResolvedCallbacks.push(() => {  // todo...  onFulfilled(this.value)  });  this.onRejectedCallbacks.push(() => {  // todo...  onRejected(this.reason);  })  }  } } 复制代码

注意:在 then 方法中,并无往队列中直接插入回调函数, 而是使用函数包装后再 push,是为了方便后续扩展 ( eg:获取并处理 onFulfilled() 的返回值)

到如今为止,实现了基础版 Promise , 但看着和以前的 callback 只是写法上不一样,并无体现出 Promise 的优点,接下来,继续探索 Promise 中的高级特性

2.2 高级特性

2.2.1 实现 then 链式调用

对于实例上的 then(onFulfilled, onRejected) 方法,其参数为成功、失败两个回调函数。总结出如下几个使用场景

  • 若是两个方法执行返回值是普通值,则会被传递到外层的下一个 then
  • 若是两个方法执行过程当中抛出异常,则会在下一个 then 的失败回调中捕获异常
  • 当两个方法执行返回值是 promise, 那么会用该 promise 的状态做为结果 ( promise 的状态是“成功”,则会调用下一个 then 的成功回调;状态为“失败”则会调用下一个 then 的失败回调)
  • 错误处理,当发生错误时( then 中抛错或返回一个失败的 promise ),该错误会被最近的一个失败回调捕获,当该失败回调执行后,能够继续调用 then 方法

在 Promise 中,promise.then 链式调用的实现原理是经过返回一个新的 promise 来实现的

「思考题」为何返回新的 promise, 而不是使用原来的 promise?

由于 promise 的状态一旦"成功"或"失败"了,就不能再改变了,因此只能返回新的 promise,这样才能够继续调用下一个then 中的成功/失败回调

接下来,须要实现如下几点

  • 调用 then 方法,建立一个新的 promise, 最后将这个新 promise 返回
  • 须要获取 then 方法中 onFulfilledonRejected 回调函数的返回值,经过新的 promise 传递到下一个 then 方法中
class Promise {
 //....  then(onFulfilled, onRejected) {  // 新的 promise  let promise2 = new Promise((resolve, reject) => {})  if (this.status == ENUM.FULFILLED) {  let x = onFulfilled(this.value)  }  if (this.status == ENUM.REJECTED) {  let x = onRejected(this.reason)  }  if (this.status === ENUM.PENDING) {  this.onResolvedCallbacks.push(() => {  let x = onFulfilled(this.value)  });  this.onRejectedCallbacks.push(() => {  let x = onRejected(this.reason);  })  }  return promise2  } } 复制代码

如今,须要将回调函数执行的返回值 x 传递到下一个 then 方法中,是传递到下一个 then 方法中的成功回调,仍是失败回调?须要根据 x 的值来判断。

  • x 是普通值,将经过 promise2 中的 resolve 传递给成功回调;
  • x 是个 Error,则经过 promise2 中的 reject 传递给失败回调;
  • 固然 x 也又有多是个 promise 实例,因此都须要考虑到。

由于须要使用 promise2 中的 resolve, reject 传递 x (两个方法在外部没法获取到), 同时new Promise(executor) 时,executor 是当即执行,因此,将整个 then 方法中的逻辑放到 executor 函数中执行,就能够访问到 resolve, reject 方法了

class Promise {
 //....  then(onFulfilled, onRejected) {  // 新的 promise  let promise2 = new Promise((resolve, reject) => {  if (this.status == ENUM.FULFILLED) {  // onFulfilled 执行可能报错,使用 try...catch...捕获  try{  let x = onFulfilled(this.value)  resolve(x)  } catch (e){  reject(e)  }  }  // ...  })  return promise2  } } 复制代码

由于返回值 x 存在多种状况, 因此将判断逻辑抽离到外部函数 resolvePromise

class Promise {
 //....  then(onFulfilled, onRejected) {  // 新的 promise  let promise2 = new Promise((resolve, reject) => {  if (this.status == ENUM.FULFILLED) {  try{  let x = onFulfilled(this.value)  resolvePromise(x, promise2, resolve, reject)  } catch (e){  reject(e)  }  }  // ...  })  return promise2  } } const resolvePromise = (x, promise2, resolve, reject) => {  } 复制代码

相信仔细的小伙伴已经发现,在 new Promise 还没结束就访问 promise2 确定会报错。只需将 resolvePromise 变成异步代码执行就能够访问到 promise2

//...
if (this.status == ENUM.FULFILLED) {  setTimeout(() => {  try {  let x = onFulfilled(this.value)  resolvePromise(x, promise2, resolve, reject)  } catch (e) {  reject(e)  }  }, 0) } 复制代码

接下来,须要实现 resolvePromise 方法了

2.2.2 resolvePromise 方法

resolvePromise 方法主要是用来解析 x 是不是promise, 按照 Promises/A+规范: the-promise-resolution-procedure 规定,分红如下几步

函数参数 resolvePromise(x, promise2, resolve, reject)

  • (1) 若 xpromise2 引用的是同一个对象,则直接报错。(示例代码以下)
let promise = new Promise((resolve, reject) => {})
 let promise2 = promise.then(() => {  return promise2 // x 表明了then中函数的返回值,也就是 promise2 })  promise2.then(() => {}, err=> {  console.log('err:', err) })  // err: TypeError: Chaining cycle detected for promise #<Promise> (循环引用了) 复制代码
  • (2) 若 x 是一个普通值,直接经过 resolve 返回
  • (3) 若 x 是一个对象或者函数,判断 x 是否存在 then 方法,当存在 then 方法,代表 x 就是一个 promise,此时执行 then 方法
  • (4) 执行 then 方法时,有一个成功回调和一个失败回调,执行成功走成功回调,并传入成功结果 y;执行失败走失败回调,并传入失败缘由 e, 使用 reject 返回
  • (5) 执行成功返回值 y 可能仍是个 promise, 继续递归解析 y 的值
  • (6) then 的回调函数只能执行一次,要么成功,要么失败(设置标识符 called)
  • (7) 当 x 不存在 then 方法时,代表 x 是普通的对象,直接经过 resolve 返回
const resolvePromise = (x, promise2, resolve, reject) => {
 // (1)  if (x === promise2) {  reject(new TypeError(`TypeError: Chaining cycle detected for promise #<Promise>`))  }   if ((typeof x === 'object' && x !== null) || typeof x === 'function') {  let called = false // (6)  try {  const then = x.then  // (3)  if (typeof then === 'function') {  // (4)  then.call(x, y => {  // (5) y 多是个 promise  if (called) return  called = true  resolvePromise(y, promise2, resolve, reject)  }, e => {  if (called) return  called = true  reject(e)  })  } else {  // (7)  resolve(x)  }  } catch (e) {  // then 执行过程出错,也不能继续向下执行  if (called) return  called = true  reject(e)  }  } else {  // (2)  resolve(x)  } } 复制代码

如今 resolvePromise 方法已经基本实现,其中还有如下几点须要说明

  1. 为啥须要判断 x 为函数?

由于 resolvePromise 须要兼容其余人写的 promise , 别人的 promise 可能就是一个函数

  1. 执行 const then = x.then 为啥须要使用 try...catch... 捕获异常 ?

由于可使用 Object.definePropertiesProxy 改写 x.then 的返回值

  1. 执行 then 方法,为啥使用 call, 而不是直接执行 x.then() ?

能够复用上次取出来的then方法,避免二次调用 x.then()

2.2.3 值穿透

new Promise((resolve, reject) => {
 resolve(123) }).then().then().then(data => {  console.log('success:', data) }) // success: 123 复制代码

上面代码中的 123 是如何直接穿透到最后一个 then 方法中的呢?

Promises/A+规范: onFulfilled, onRejected are optional arguments , 规定 then 方法中的 onFulfilled, onRejected 是可选参数,因此咱们须要提供一个默认值

class Promise {
 // ...  then(onFulfilled, onRejected) {  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v  onRejected = typeof onRejected === 'function' ? onRejected: e => {throw e}  // ...  } } 复制代码

经过给 onFulfilled, onRejected 设置默认值就能够实现值穿透。至此,已经实现 Promises/A+ 中规范的功能,能够对代码进行规范测试了

2.3 规范测试

规范测试,首先须要安装 promises-aplus-tests npm 包,同时须要在导出 Promise 前增长下面测试代码

class Promise {
 // ... } Promise.defer = Promise.deferred = function () {  let dfd = {};  dfd.promise = new Promise((resolve,reject)=>{  dfd.resolve = resolve;  dfd.reject = reject;  });  return dfd; } module.exports = Promise; 复制代码

安装依赖

npm install promises-aplus-tests -D
复制代码

同时在 package.json 增长

"scripts": {
 "test": "promises-aplus-tests ./index.js"  }, 复制代码

最后,运行 npm run test 就能够进行测试了,测试结果以下 截屏2020-07-02上午12.52.39.png

2.4 其它方法和属性

下面介绍的内容,并非 Promises/A+ 中的规范,但咱们也能够继续探索

2.4.1 catch 方法

实例上的 catch 方法用来捕获执行过程当中产生的错误,同时返回值为 promise, 参数为一个失败回调函数,相对于执行 then(null, onRejected)

class Promise{
 // ...  catch (onErrorCallback) {  return this.then(null, onErrorCallback)  } } 复制代码

2.4.2 finally 方法

finally 的参数是一个回调函数,不管 promise 是执行成功,仍是失败,该回调函数都会执行。

应用场景有:页面异步请求数据,不管数据请求成功仍是失败,在 finally 回调函数中都关闭 loading

同时,finally 方法有如下特色

  • 值穿透。能够将前面 promise 的值传递到下一个 then 方法中,或者将错误传递到下一个 catch 方法中
  • 等待执行。当 finally 回调函数返回一个新的 promise, finally 会等待该 promise 执行结束后才处理传值
  • 若该 promise 执行成功,finally 方法将不予理会执行结果,仍是将上一个的结果传递到下一个 then
  • 若新的 promise 执行失败报错,finally 方法会将错误缘由传递到下一个 catch 方法

下面是具体代码演示

// (1) 值穿透, 请注意 finally 的回调函数是不存在参数的
Promise.resolve(100).finally((data) => {  console.log('finally: ', data) }).then(data => {  console.log('success: ', data) }).catch(err => {  console.log('error', err) }) // finally: undefined // success: 100  // (2) 等待执行 // 返回一个执行成功的 promise, 但向下传递但仍是上一次执行结果 Promise.resolve(100).finally(() => {  return new Promise((resolve, reject) => {  setTimeout(() => {  resolve(200)  }, 1000)  }) }).then(data => {  console.log('success: ', data) // success: 100 }).catch(err => {  console.log('error', err) })  // 当 promise 执行失败,则将该 promise 执行结果向下传递 Promise.reject(100).finally(() => {  return new Promise((resolve, reject) => {  setTimeout(() => {  reject(200)  }, 1000)  }) }).then(data => {  console.log('success: ', data) }).catch(err => {  console.log('error', err) // error 200 })  复制代码

在掌握了 finally 的用法后,继续探索如何实现它?

class Promise{
 finally (callback) {  return this.then(value => {  return Promise.resolve(callback()).then(() => value)  }, err => {  return Promise.resolve(callback()).then(() => {throw err})  })  } } 复制代码

2.4.3 静态方法

静态方法是那经过 Promise 来调用,而不是经过实例 promise 来调用的方法

  • Promise.resolve()、Promise.reject() 返回值:一个成功状态的 promise 、一个失败状态的 promise
class Promise{
 // ...  // 成功状态  static resolve(value){  return new Promise((resolve, reject) => {  resolve(value)  })  }  // 失败状态  static reject(reason){  return new Promise((resolve, reject) => {  reject(reason)  })  } } 复制代码

假设执行成功返回值 value 是个 promisePromise.resolve() 会对该 value 递归解析,直到该 promise 执行结束才会向下执行

class Promise{
 constructor() {  //...  const resolve = (value) => {  if (value instanceof Promise) {  // 递归解析, 直到 value 为普通值  value.then(resolve, reject)  }  // ...  }  const reject = (err) => {  // ...  }  //...  } } 复制代码

如今,执行下面代码,就能够正常获取数据了

Promise.resolve(new Promise((resolve, reject) => {
 setTimeout(() => {  resolve('hello')  }, 2000) })).then(data => {  console.log(data) // hello }) 复制代码
  • Promise.all()

解决并发问题,多个异步并发并获取最终的结果。

参数是一个 promise数组,当数组中每一项都执行成功,结果就是成功,反之,有一个失败,结果就是失败。

class Promise {
 static all(arrList) {  if (!Array.isArray(arrList)) {  const type = typeof arrList;  return new TypeError(`TypeError: ${type} ${arrList} is not iterable`)  }  return new Promise((resolve, reject) => {  const backArr = []  const count = 0  const processResultByKey = (value, index) => {  backArr[index] = value  if (++count === arrList.length) {  resolve(backArr)  }  }  for (let i = 0; i < arrList.length; i++) {  const item = arrList[i];  if (item && item.then === 'function') {  item.then((value) => {  processResultByKey(value, i)  }, reject)  } else {  processResultByKey(item, i)  }  }  })  } } 复制代码

⚠️注意:在 all 方法中,是经过 ++count === arrList.length (count 为计数器) 来判断是否所有执行完成,而不是使用 index === arrlist.length - 1 来判断,具体缘由以下

// p1 为 promise 实例
Promise.all([1,2, p1, 4]).then(data => {})  // 当执行数组最后一项时,index === arrlist.length - 1 表达式成立, // 就会执行 resolve 返回执行结果, // 但此时的 p1 可能还没执行结束,因此使用计数器来判断 复制代码
  • Promise.race()

all 方法不一样的是,Promise.race 采用最早成功或最早失败的做为执行结果

class Promise {
 static race(arrList) {  return new Promise((resolve, reject) => {  for (let i = 0; i < arrList.length; i++) {  const value = arrList[i];  if (value && value.then === 'function') {  value.then(resolve, reject)  } else {  resolve(value)  }  }  })  } } 复制代码

Promise.race 的主要应用场景以下

  • (基础)多个请求采起最快的 (eg: 小飞机的多个代理线路,哪条线路的响应速度最快,就使用哪条)
  • (高级)封装中断方法,中断 promise 的执行 (异步请求设置超时时间,当超时后,异步请求就会被迫失败)

原生的 promise 上并无 abort (中止、中断) 方法,假设使用场景以下

const p1 = new Promise((resolve, reject) => {
 setTimeout(() => { // 模拟异步请求,5s 后返回  resolve('hello')  }, 5000) })  const newP = wrap(p1) setTimeout(() => { // 设置超时时间,超时后,调用 newP.abort  newP.abort('请求超时了') }, 4000)  newP.then(data => {}).catch(err => {}) 复制代码

newP1 是一个具备 abort 方法的 promise, 超时后就调用 newP.abort()

如今须要实现 wrap 封装方法,传入一个普通 promise 实例,返回一个具备 abort 方法的 promise 实例

const wrap = (promise) => {
 let abort  let newPromise = new Promise((resolve, reject) => {  abort = reject  })  let p = Promise.race([promise, newPromise])  p.abort = abort  return p } 复制代码

wrap 方法就是利用 Promise.race 采用最快的做为执行结果这一特性,来看 promise, newPromise 哪一个最早执行,而 newPromise 的执行,是经过外部调用 abort 来实现的

3、Promise 的扩展

⚠️注意:如下对 Promise 的扩展仅适用于 Node 环境

3.1 promisify

功能:把 node 中的一个 api 转换成promise的写法, 以 fs.readFile 读取文件为例

常规写法

const fs = require('fs) fs.readFile('./name.json', (err, data) => {}) 复制代码

缺点:回调地狱嵌套

改为 promisify 链式调用写法

const util = require('util')
 const read = util.promisify(fs.readFile) read('./name.json').then(data => console.log(data)) 复制代码

特色promisify 方法特色以下

  • 返回一个函数,函数执行后才返回 promise
const promisify = fn => {
 return (...args) => {  return new Promise((resolve, reject) => {  fn(...args, (err, data) => {  if (err) reject(err)  resolve(data)  })  })  } } 复制代码
  • promisify 函数中,执行 fn 函数时,能够手动添加了回调函数是由于 node 中大部分的方法的回调都是这种格式

3.2 bluebird

promisify 方法每次只能修改一个方法,而第三方的库 bluebird 中实现了 promisifyAll 方法,能够将某个对象下全部的方法转换成 promise 写法

const fs = require('fs')
const bluebird = require('bluebird'); // 第三方库,需提早安装 const newFs = bluebird.promisifyAll(fs); newFs.readFileAsync('./name.txt', 'utf-8').then(data => {}).catch(err => {}) 复制代码

promisifyAll() 特色以下

  • 函数参数为对象,会将对象上全部的方法,增长一个 Async** **后缀,变成 promise 写法
  • 并无覆盖原方法,只是扩展
const promisifyAll = (target) {
 Reflect.ownKeys(target).forEach(key => {  target[`${key}Async`] = promisify(target[key])  })  return target } 复制代码

Reflect 对象是 ES 中内置对象,它提供拦截 JavaScript 操做的方法 Reflect | MDN, 此处,也可以使用 Object.keys() 。同时,使用了前面的 promisify 来改写方法

3.3 原生 Node 支持

目前,在高版本浏览器中,已经对 api 集成了 promise 的写法,使用以下

const fs = require('fs').promises
fs.readFile('./name.txt', 'utf-8').then(data => {}) 复制代码

正由于原生的支持,致使第三方的一些扩展再也不流行

4、参考资源

5、后语

本文使用 mdnice 排版

相关文章
相关标签/搜索