手写系列-这一次,彻底搞懂 Promise

2023-10-17 15:57:20 浏览数 (2)

一、前言

想要实现 Promise,必须先了解 Promise 是什么,以及 Promise 有哪些功能。

还不是特别了解 Promise 的同学,建议先移步 ES6入门-Promise 熟悉。

Promise 是基于 Promises/A 规范 实现的,换句话说,我们可以按照 Promises/A 规范 来手写 Promise。

1.1 小例子

Promise,直译过来就是承诺,Promise 到底承诺了什么呢?

当我在麦当劳点一份汉堡套餐,收银员会给我一张收据,这个收据就是 Promise,代表我已经付过钱了,麦当劳会为我做一个汉堡套餐的承诺,我要通过收据来取这个汉堡套餐。

那么这个买汉堡得到的承诺会有以下 3 种状态:

  1. 等待状态:我刚下单,汉堡还没做好,这时我可以在等待汉堡时,同时做其他事情;
  2. 成功状态:汉堡做好了,通知我取餐;
  3. 失败状态:发现卖完了,通知我退款;

需要注意的是,状态的修改是不可逆的,当汉堡做好了,承诺兑现了,就不能再回到等待状态了。

总结一下,Promise 就是一个承诺,承诺会给你一个处理结果,可能是成功的,可能是失败的,而返回结果之前,你可以同时做其他事情。

二、Promises/A

接下来,按照 Promises/A 规范 一步步实现 Promise。

1. Promise 基本用法

先瞅一眼 ES6 Promise 基本用法。

代码语言:javascript复制
const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value)
  } else {
    reject(error)
  }
});

promise.then(data => {
    console.log('请求成功')
}, err => {
    console.log('请求失败')
})

1.1 Promise 状态

Promise 拥有自己的状态,初始状态->成功状态时,执行成功回调,初始状态->失败状态时,执行失败回调。

  1. pending:初始状态,可以转换为 fulfilledrejected 状态;
  2. fulfilled:成功状态,转换到该状态时必须有成功返回值,且不能再次转换状态;
  3. rejected:失败状态,转换到该状态时必须有错误原因,且不能再次转换状态;

通过已知的 Promise 3 种状态,可定义常量 STATUS 和 MyPromise 状态 status。

代码如下:

代码语言:javascript复制
// Promise 3 种状态
const STATUS = {
  PENDING: 'pending',
  FULFILLED: 'fulfilled',
  REJECTED: 'rejected'
}

class MyPromise {
    // 初始状态为 pending
    status = STATUS.PENDING
}

1.2 执行器

从基本用法可知,Promise 需要接收 1 个执行器函数作为参数,这个函数带有 2 个参数。

  1. resolve:把 Promise 状态改成成功;
  2. reject:把 Promise 状态改成失败;

代码如下:

代码语言:javascript复制
class MyPromise {
  constructor (executor) {
      // 执行器
      executor(this.resolve, this.reject)
  }
  // 成功返回值
  value = null

  // 失败返回值
  reason = null

  // 修改 Promise 状态,并定义成功返回值
  resolve = value => {
      if (this.status === STATUS.PENDING) {
          this.status = STATUS.FULFILLED
          this.value = value
      }
  }

  // 修改 Promise 状态,并定义失败返回值
  reject = reason => {
      if (this.status === STATUS.PENDING) {
              this.status = STATUS.REJECTED
              this.reason = reason
          }
      }
    }
}

1.3 then

Promise 拥有 then 方法,then 方法第一个参数是成功状态的回调函数 onFulfilled,第二个参数是失败状态的回调函数 onRejected

代码语言:javascript复制
promise.then(onFulfilled, onRejected)

onFulfilled 要求如下:

  • 必须在 promise 状态为完成时调用它,并将 promise 的 value 作为它的第一个参数;
  • 在 promise 完成之前不能调用它;
  • 它不能被多次调用;

onRejected 要求如下:

  • 必须在 promise 被拒绝后调用它,以 promise.reason 作为它的第一个参数;
  • 在 promise 被拒绝之前不能调用它;
  • 它不能被多次调用;

代码如下:

代码语言:javascript复制
class MyPromise {
    then = function (onFulfilled, onRejected) {
      if (this.status === STATUS.FULFILLED) {
          onFulfilled(this.value)
      } else if (this.status === STATUS.REJECTED) {
          onRejected(this.reason)
      }
    }
}

1.4 试试看

按照 Promise 的基本用法,创建 MyPromise 实例 mypromise。

代码语言:javascript复制
const mypromise = new MyPromise((resolve, reject) => {
  resolve('成功')
})

mypromise.then(data => {
  console.log(data, '请求成功') // 成功打印“成功 请求成功”
}, err => {
  console.log(err, '请求失败')
})

2. Promise.then

下文将按照 Promises/A 规范 完善 MyPromise.then 方法。

Promises/A 规范 中标明 then 有以下要求:

1. 可选参数

onFulfilled、onRejected 是可选参数。

  • 如果 onFulfilled 不是函数,则必须忽略它;
  • 如果 onRejected 不是函数,则必须忽略它;

代码如下:

代码语言:javascript复制
class MyPromise {
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
        onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }
    }
}

2. 多次调用 then

then 可以在同一个承诺上多次调用。

  • 当 promise 完成,所有相应的 onFulfilled 回调必须按照它们的原始调用的顺序执行 then;
  • 当 promise 被拒绝,所有相应的 onRejected 回调必须按照它们对 的原始调用的顺序执行 then;
2.1 数组缓存回调

可以理解为将 onFulfilled、onRejected 作为数组存储在 MyPromise 中,然后按照顺序执行。

代码如下:

代码语言:javascript复制
class MyPromise {
  // 成功回调
  onFulfilledCallback = []

  // 失败回调
  onRejectedCallback = []

  // 修改 Promise 状态,并定义成功返回值
  resolve = value => {
    if (this.status === STATUS.PENDING) {
        this.status = STATUS.FULFILLED
        this.value = value

        while(this.onFulfilledCallback.length) {
            this.onFulfilledCallback.shift()(value)
        }
    }
  }

  // 修改 Promise 状态,并定义失败返回值
    reject = reason => {
        if (this.status === STATUS.PENDING) {
            this.status = STATUS.REJECTED
            this.reason = reason

            while(this.onRejectedCallback.length) {
                this.onRejectedCallback.shift()(reason)
            }
        }
    }

    then = function (onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
        if (this.status === STATUS.PENDING) {
          this.onFulfilledCallback.push(onFulfilled)
          this.onRejectedCallback.push(onRejected)
        } else if (this.status === STATUS.FULFILLED) {
            onFulfilled(this.value)
        } else if (this.status === STATUS.REJECTED) {
            onRejected(this.reason)
        }
    }
}

由此,我们已实现了一个基础的 Promise。

2.2 试试看

看了这么久,试一试 MyPromise 是否符合要求吧。

代码如下:

代码语言:javascript复制
const mypromise = new MyPromise((resolve, reject) => {
  resolve('成功')
})

mypromise.then(data => {
  console.log(data, '1')
})

mypromise.then(data => {
  console.log(data, '2')
})

输出结果如图:

由图可知,和预期一样。

3. 链式调用 then

then 必须返回一个 Promise 来支持链式调用 Promise。

示例代码如下:

代码语言:javascript复制
mypromise.then(data => {
  console.log(data, '请求成功')
  return '2'
}).then(data => {
  console.log(data, '请求成功')
  return '3'
})
3.1 改写 then 方法

改动点如下:

  • then 方法需要返回 MyPromise 实例;
  • then 内部调用回调时,需通过 resolvePromise 方法判断返回值 x 的类型来处理返回值。
代码语言:javascript复制
class MyPromise {
  then = function (onFulfilled, onRejected) {
    // 返回 MyPromise实例
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.status === STATUS.PENDING) {
        this.onFulfilledCallback.push(() => {
          const x = onFulfilled(this.value)
          resolvePromise(promise2, x, resolve, reject)
        })
        this.onRejectedCallback.push(() => {
          const x = onRejected(this.reason)
          resolvePromise(promise2, x, resolve, reject)
        })
      } else if (this.status === STATUS.FULFILLED) {
        const x = onFulfilled(this.value)
        resolvePromise(promise2, x, resolve, reject)
      } else if (this.status === STATUS.REJECTED) {
        const x = onRejected(this.error)
        resolvePromise(promise2, x, resolve, reject)
      }
    }) 

    return promise2
  }
}

上述代码引用了 resolvePromise 来处理 Promise.then 的返回值

3.1.1 注意

这里的promise2暂时还不能正常运行,可能会报错:Cannot access 'promise2' before initialization | 不能访问 promise2 在初始化之前。

原因:在 new promise 时,promise2 还没有完成初始化。所以 resolvePromise 中不能访问到 promise2,在当前的执行上下文栈中,onFulfilled 或 onRejected 是不能被直接调用的,onFulfilled 或 onRejected 得是在当前事件循环后异步执行的。

解决方法:可以使用 setTimeout、setImmediate、MutationObserever、process.nextTick在 then 方法被调用后将创建一个新的栈,这个我们后续处理,先正常往下看。

3.2 resolvePromise

Promises/A 规范 对resolvePromise 的要求如下:

  • 如果 promise2 === x, 执行 reject,错误原因为 TypeError
  • 如果 x 是函数或对象
    • 如果 x.then 是函数
      • 执行 x.then
    • 如果 x.then 不是函数
      • 执行 resolve(x)
  • 如果 x 不是函数或对象
    • 执行 resolve(x)

代码如下:

代码语言:javascript复制
function resolvePromise (promise2, x, resolve, reject) {
  // 如果 promise2 === x, 执行 reject,错误原因为 TypeError
    if (promise2 === x) {
      reject(new TypeError('The promise and the return value are the same'))
    }

    // 如果 x 是函数或对象
    if ((typeof x === 'object' && x != null) || typeof x === 'function') {
      let then
      try {
        then = x.then
      } catch (error) {
        reject(error)
      }

      // 如果 x.then 是函数
      if (typeof then === 'function') {
        // Promise/A  2.3.3.3.3 只能调用一次
        let called = false
        try {
          then.call(x, y => {
            // resolve的结果依旧是promise 那就继续解析 | 递归解析
            if (called) return
            called = true
            resolvePromise(promise2, y, resolve, reject)
          }, err => {
            if (called) return
            called = true
            reject(err)
          })
        } catch (error) {
          if (called) return
          reject(error)
        }
      } else {
          // 如果 x.then 不是函数
          resolve(x)
      }
    } else {
        // 如果 x 不是 promise 实例
        resolve(x)
    }
}
3.3 试一试

试试看能不能符合预期,链式调用 then 吧。

输出结果为:

成功符合预期!

4. 异步事件

Promises/A 规范 要求 onFulfilled、onRejected 在执行上下文堆栈之前不得调用。也就是3.1.1标明要注意的点。

4.1 事件队列

当遇到一个异步事件后,并不会一直等待异步事件返回结果,而是会将这个事件挂在与执行栈不同的队列中,我们称之为事件队列。

当所有同步任务执行完成后,系统才会读取”事件队列”。

事件队列中的事件分为宏任务和微任务:

  1. 宏任务:浏览器/Node发起的任务,如 window.setTimeout;
  2. 微任务:Js 自身发起的,如 Promise;

事件队列就是先执行微任务,再执行宏任务,而宏任务和微任务包含以下事件:

宏任务

微任务

setTimeout

Promise

setInterval

queueMicrotask

script(整体代码块)

-

看看下面这个例子,你知道答案吗?

代码语言:javascript复制
setTimeout(function () {
  console.log(1)
})
new Promise(function(resolve,reject){
  console.log(2)
  resolve(3)
}).then(function(val){
  console.log(val)
})
console.log(4)

打印结果的顺序是2->4->3->1。事件队列如下:

主队列,同步任务,new Promise 内部的同步任务

代码语言:javascript复制
new Promise(function(resolve,reject){
  console.log(2)
})

主队列,同步任务,new Promise 后的 console.log(4)

代码语言:javascript复制
console.log(4)

异步任务的微任务

代码语言:javascript复制
promise.then(function(val){
  console.log(val)
})

异步任务的宏任务

代码语言:javascript复制
setTimeout(function () {
  console.log(1)
})

因此,想要实现 onFulfilled、onRejected 在执行上下文堆栈之前不得调用,我们需要把 onFulfilled、onRejected 改造成微任务,这里使用 queueMicrotask 来模拟实现微任务,代码如下:

代码语言:javascript复制
class MyPromise {
  then (onFulfilled, onRejected) {
    const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    const realOnRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }

    const promise2 = new MyPromise((resolve, reject) => {

      const fulfilledMicrotask = () =>  {
        // 创建一个微任务等待 promise2 完成初始化
        queueMicrotask(() => {
          try {
            // 获取成功回调函数的执行结果
            const x = realOnFulfilled(this.value)
            // 传入 resolvePromise 集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          } 
        })  
      }

      const rejectedMicrotask = () => { 
        // 创建一个微任务等待 promise2 完成初始化
        queueMicrotask(() => {
          try {
            // 调用失败回调,并且把原因返回
            const x = realOnRejected(this.reason)
            // 传入 resolvePromise 集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          } 
        }) 
      }

      if (this.status === STATUS.PENDING) {
        this.onFulfilledCallbacks.push(fulfilledMicrotask)
        this.onRejectedCallbacks.push(rejectedMicrotask)
      } else if (this.status === STATUS.FULFILLED) {
        fulfilledMicrotask()
      } else if (this.status === STATUS.REJECTED) {
        rejectedMicrotask()
      }
    }) 

    return promise2
  }
}
4.2 试试看

打印结果如图:

成功按顺序打印。

3. Promise/A 测试

下面将用 Promise/A 测试工具 promises-aplus-tests 测试我们手写的 Promise 是否符合规范。

3.1 安装 promises-aplus-tests

代码语言:javascript复制
npm install promises-aplus-tests -D

3.2 为 MyPromise 添加 deferred

代码语言:javascript复制
MyPromise {
  ......
}

MyPromise.deferred = function () {
  var result = {};
  result.promise = new MyPromise(function (resolve, reject) {
    result.resolve = resolve
    result.reject = reject
  });

  return result;
}
module.exports = MyPromise

3.3 配置启动命令

代码语言:javascript复制
"scripts": {
  "test:promise": "promises-aplus-tests ./Promise/index"
}

3.4 开始测试

代码语言:javascript复制
npm run test:promise

哇哦,全部成功!!

3.5 完整代码

截止到此,完整代码如下:

代码语言:javascript复制
const STATUS = {
  PENDING: 'pending',
  FULFILLED: 'fulfilled',
  REJECTED: 'rejected'
}

class MyPromise {

  constructor (executor) {
    // 执行器
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }

  // 初始状态为 pending
  status = STATUS.PENDING

  // 成功返回值
  value = null

  // 失败返回值
  reason = null

  // 成功回调
  onFulfilledCallbacks = []

  // 失败回调
  onRejectedCallbacks = []

  // 修改 Promise 状态,并定义成功返回值
  resolve = value => {
    if (this.status === STATUS.PENDING) {
      this.status = STATUS.FULFILLED
      this.value = value
      // this.onFulfilledCallbacks.forEach(fn => fn())
      while(this.onFulfilledCallbacks.length) {
        this.onFulfilledCallbacks.shift()(value)
      }
    }
  }

  // 修改 Promise 状态,并定义失败返回值
  reject = reason => {
    if (this.status === STATUS.PENDING) {
      this.status = STATUS.REJECTED
      this.reason = reason
      // this.onRejectedCallbacks.forEach(fn => fn())
      while(this.onRejectedCallbacks.length) {
        this.onRejectedCallbacks.shift()(reason)
      }
    }
  }

  then = function (onFulfilled, onRejected) {
    // onFulfilled、onRejected 是可选参数 不是函数则必须忽略它
    const realOnFulfilled = onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    const realOnRejected = onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }

    const promise2 = new MyPromise((resolve, reject) => {
      // 报错:Cannot access 'promise2' before initialization | 不能访问 promise2 在初始化之前
      // 原因:在 new promise 时,promise2 还没有完成初始化,所以 resolvePromise 中不能访问到 promise2
      // 在当前的执行上下文栈中,onFulfilled 或 onRejected 是不能被直接调用的
      // onFulfilled 或 onRejected 得是在当前事件循环后异步执行的
      // 可以使用 setTimeout、setImmediate、MutationObserever、process.nextTick在 then 方法被调用后将创建一个新的栈

      const fulfilledMicrotask = () => {
        // 创建一个微任务等待 promise2 完成初始化
        queueMicrotask(() => {
          try {
            // 获取成功回调函数的执行结果
            const x = realOnFulfilled(this.value)
            // 传入 resolvePromise 集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          } 
        })
      }

      const rejectedMicrotask = () => {
        queueMicrotask(() => {
          try {
            // 获取成功回调函数的执行结果
            const x = realOnRejected(this.reason)
            // 传入 resolvePromise 集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }

      if (this.status === STATUS.PENDING) {
        this.onFulfilledCallbacks.push(fulfilledMicrotask)
        this.onRejectedCallbacks.push(rejectedMicrotask)
      } else if (this.status === STATUS.FULFILLED) {
        fulfilledMicrotask()
      } else if (this.status === STATUS.REJECTED) {
        rejectedMicrotask()
      }
    })

    return promise2
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  // 如果 promise2 === x 执行 reject,错误原因为 TypeError
  if (promise2 === x) {
    reject(new TypeError('The promise and the return value are the same'))
  }

  // 如果 x 是函数或对象
  if ((typeof x === 'object' && x != null) || typeof x === 'function') {
    let then
    try {
      then = x.then
    } catch (error) {
      reject(error)
    }

    // 如果 x.then 是函数
    if (typeof then === 'function') {
      // Promise/A  2.3.3.3.3 只能调用一次
      let called = false
      try {
        then.call(x, y => {
          // resolve的结果依旧是promise 那就继续解析 | 递归解析
          if (called) return
          called = true
          resolvePromise(promise2, y, resolve, reject)
        }, err => {
          if (called) return
          called = true
          reject(err)
        })
      } catch (error) {
        if (called) return
        reject(error)
      }
    } else {
      // 如果 x.then 不是函数
      resolve(x)
    }
  } else {
    // 如果 x 是个普通值就直接返回 resolve 作为结果  Promise/A  2.3.4  
    resolve(x)
  }
}

// 测试 Promise 是否符合规范
MyPromise.deferred = function () {
  var result = {}
  result.promise = new MyPromise(function (resolve, reject) {
    result.resolve = resolve
    result.reject = reject
  })

  return result
}
module.exports = MyPromise

下面的完善Promise 的 API将基于此基础代码。

4. Promise 的 API

虽然上述的 promise 源码已经符合 Promise/A 的规范,但是原生的 Promise 还提供了一些其他方法,如:

  • Promise.resolve()
  • Promise.reject()
  • Promise.prototype.catch()
  • Promise.prototype.finally()
  • Promise.all()
  • Promise.race()

下面具体说一下每个方法的实现:

4.1 Promise.prototype.catch

Promise.prototype.catch 用来捕获 promise 的异常,就相当于一个没有成功的 then

至于为什么先实现此方法,是为了防止实现其他 api 时会报错。

代码语言:javascript复制
// ...
catch(errCallback) {
  return this.then(null,errCallback)
}
// ...

4.2 Promise.prototype.finally

finally 表示不是最终的意思,而是无论如何都会执行的意思。 如果返回一个 promise 会等待这个 promise 也执行完毕。如果返回的是成功的 promise,会采用上一次的结果;如果返回的是失败的 promise,会用这个失败的结果,传到 catch 中。

代码语言:javascript复制
finally(callback) {
  return this.then((value)=>{
    return MyPromise.resolve(callback()).then(()=>value)
  },(reason)=>{
    return MyPromise.resolve(callback()).then(()=>{throw reason})
  })  
}

测试一下

代码语言:javascript复制
MyPromise.resolve(456).finally(()=>{
  return new MyPromise((resolve,reject)=>{
    setTimeout(() => {
        resolve(123)
    }, 3000)
  })
}).then(data=>{
  console.log(data,'success')
}).catch(err=>{
  console.log(err,'error')
})

控制台等待 3s 后输出:456 'success',相反如果把resolve(123)改为reject(123) 等待 3s后输出123 'error'

4.3 Promise.resolve

默认产生一个成功的 promise。

代码语言:javascript复制
static resolve(data){
  return new MyPromise((resolve,reject)=>{
    resolve(data)
  })
}

这里需要注意的是,promise.resolve 是具备等待功能的。如果参数是 promise 会等待这个 promise 解析完毕,在向下执行,所以这里需要在原来 resolve 方法中做一个小小的处理:

代码语言:javascript复制
// 修改 Promise 状态,并定义成功返回值
resolve = value => {
  if(value instanceof MyPromise){
    // 递归解析 
    return value.then(this.resolve,this.reject)
  }
  if (this.status === STATUS.PENDING) {
    this.status = STATUS.FULFILLED
    this.value = value
    while(this.onFulfilledCallbacks.length) {
      this.onFulfilledCallbacks.shift()(value)
    }
  }
}

4.4 Promise.reject

代码语言:javascript复制
static reject(reason){
  return new MyPromise((resolve,reject)=>{
    reject(reason)
  })
}

4.5 Promise.all

Promise.all 是解决并发问题的,多个异步并发获取最终的结果(如果有一个失败则失败)。

Promise.all方法可以接收一个promise数组作为参数,返回一个新的promise对象,该对象在数组中所有promise都成功时才会被resolve。如果其中有一个promise失败,则Promise.all会立即将其reject,并且不再等待其他promise的执行结果。

注意:这个参数数组里面也不是必须都是promise,也可以是常量普通值。

代码语言:javascript复制
static all(values) {
  if (!Array.isArray(values)) {
    const type = typeof values
    return new TypeError(`TypeError: ${type} ${values} is not iterable`)
  }

  return new MyPromise((resolve, reject) => {
    let resultArr = []
    let orderIndex = 0
    const processResultByKey = (value, index) => {
      resultArr[index] = value
      if (  orderIndex === values.length) {
        resolve(resultArr)
      }
    }
    for (let i = 0; i < values.length; i  ) {
      let value = values[i]
      if (value && typeof value.then === 'function') {
        value.then((value) => {
          processResultByKey(value, i)
        }, reject)
      } else {
        processResultByKey(value, i)
      }
    }
  })
}

测试一下:

代码语言:javascript复制
let p1 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok1')
  }, 1000)
})

let p2 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok2')
  }, 1000)
})

MyPromise.all([1,2,3,p1,p2]).then(data => {
  console.log('resolve', data)
}, err => {
  console.log('reject', err)
})

控制台等待 1s 后输出:resolve (5) [1, 2, 3, 'ok1', 'ok2']

4.6 Promise.race

Promise.race 用来处理多个请求,采用最快的(谁先完成用谁的)。

代码语言:javascript复制
static race(promises) {
  return new MyPromise((resolve, reject) => {
    // 一起执行就是for循环
    for (let i = 0; i < promises.length; i  ) {
      let val = promises[i]
      if (val && typeof val.then === 'function') {
        val.then(resolve, reject)
      } else { // 普通值
        resolve(val)
      }
    }
  })
}

特别需要注意的是:因为Promise 是没有中断方法的,xhr.abort()、ajax 有自己的中断方法,axios 是基于 ajax 实现的;fetch 基于 promise,所以他的请求是无法中断的。

这也是 promise 存在的缺陷,我们可以使用 race 来自己封装中断方法:

代码语言:javascript复制
function wrap(promise) {
  // 在这里包装一个 promise,可以控制原来的promise是成功还是失败
  let abort
  let newPromise = new MyPromise((resolve, reject) => { // defer 方法
      abort = reject
  });
  let p = MyPromise.race([promise, newPromise]) // 任何一个先成功或者失败 就可以获取到结果
  p.abort = abort
  return p
}

const promise = new MyPromise((resolve, reject) => {
  setTimeout(() => { // 模拟的接口调用 ajax 肯定有超时设置
    resolve('成功')
  }, 1000);
});

let newPromise = wrap(promise)

setTimeout(() => {
  // 超过3秒 就算超时 应该让 proimise 走到失败态
  newPromise.abort('超时了')
}, 3000)

newPromise.then((data => {
  console.log('成功的结果'   data)
})).catch(e => {
  console.log('失败的结果'   e)
})

控制台等待 1s 后输出:成功的结果成功

5.总结

以上,我们实现了一个符合 Promises/A 规范 的 Promise,也实现了Promise一些常用API方法。

总结一下 Promise 其实就是一个帮助我们执行异步任务的对象,因为 Javascript 单线程的特性,导致必须通过为异步任务添加回调来得到异步任务的结果。为了解决回调地狱,Promise 应运而生。

Promise 通过对异步任务执行状态的处理,让我们可以在 Promise.then 中获取任务结果,让代码更加清晰优雅。

Promise.then 的链式调用,以顺序的方式来表达异步流,让我们更好的维护异步代码。

注:该文基于 https://juejin.cn/post/6973155726302642206 基础上转载修改完善,感谢。

0 人点赞