Promise 一览

Promise 是什么?

Promise 是承诺的意思,在未来将兑现结果,可能成功也可能失败。在 Javascript 中,Promise 是一个内置对象,可以用来处理异步任务。比如一个数据请求,可能需要时间等待,但我们的程序不能卡住等待数据返回,于是通过 promise 提前设置好数据返回后将要做的操作,这样就不会卡住程序了,同时又能保证数据的处理。比如:

const promise = new Promise((resolve, reject) => {
  // 这里模拟数据获取,1秒后拿到数据
  setTimeout(() => {
    resolve('data after one second')
  }, 1000)
})

promise.then(res => {
  // 这里拿到数据之后进行处理
  console.log(res)
})

console.log('deal with other thing')

上面的例子,我们会先打印 deal with other thing, 1 秒后 打印 data after one second, 这就是 Promise, “预约”将会做的事情,而不阻塞当前该做的事情。

Promise 为什么?

上面的 Promise 看起来平平无奇,为啥需要它呢?这得回顾过去,处理异步事件,靠什么方法实现。

没错,你一定已经知道了,就是回调函数。

在 Javascript ,“函数是一等公民”, 可以作为参数,可以作为返回值进行传递。那么我们可以把异步事件之后需要处理的操作,封装成一个函数,传递,在拿到数据之后进行调用,比如:

function handler(data) {
  console.log(data);
}

function getDataOneSecond(callback) {
  // 这里模拟数据获取,1秒后拿到数据
  setTimeout(() => {
    callback('data after one second')
  }, 1000)
}

getDataOneSecond(handler)

console.log('deal with other thing')

上面的例子,同样实现了异步事件的处理而不阻塞程序。但是有明显的不足,比如代码复杂了,理解起来有点“绕”。再者,如果我们的异步事件不止一个,且有前后顺序处理的关系,那么事情就更加复杂起来,回调之后再回调,不用举例你也能想象代码会嵌套起来,令人恶心,所谓“回调地狱”。

相比回调函数,我们可以看到,Promise 明显比较符合事务前后关系,异步事件之后的操作,放到了 then 里面。

而在处理有前后关系的多个异步事件,Promise 也有对应的处理方式:then 的链式调用,比如:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('data after one second')
  }, 1000)
})

promise
  .then(res => {
    console.log(res)
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(res + ' and another second')
      }, 1000)
    })
  })
  .then(res => {
    console.log(res)
  })

由于 then 本身还会返回一个新的 Promise 实例,所以可以接着调用 then方法, 这样就避免了回调地狱的问题,以线性的方法编写我们的多个异步事件的代码了

Promise 怎么实现的?

Promise 本身是由 Javascript 引擎实现的, 属于微任务。在每一次的事件循环中,等宏任务执行之后,会处理微任务、清空微任务队列。then 方法会在微任务队列加入一个事件。

那么让你模拟创建一个构造函数,实现和 Promise 类似的功能,将怎么实现呢?
这里我们来实现一个最基础简化版本的伪 Promise:

// 构造函数
function MyPromise(excutor) {
  this.state = 'PENDING'; // 'FULFILLED'  'REJECTED'
  this.value = null;
  this.reason = null;
  this.taskResolveList = [];
  this.taskRejectList = [];
  const self = this;

  function resolve (value) {
    if (self.state === 'PENDING') {
      self.value = value
      self.state = 'FULFILLED'
      if (self.taskResolveList.length) {
        self.taskResolveList.forEach(cb => cb())
      }
    }
  }

  function reject(reason) {
    if (self.state === 'PENDING') {
      self.reason = reason
      self.state = 'REJECTED'
      if (self.taskRejectList.length) {
        self.taskRejectList.forEach(cb => cb())
      }
    }
  }
  try {
    excutor(resolve, reject)
  } catch(error) {
    reject(error)
  }
}

MyPromise.prototype.then = function (fulfilled, rejected) {

  if (this.state === 'FULFILLED') {
    // 这里应该放到微任务掉列里面,但无法实现,以同步执行代替
    fulfilled(this.value)
  }

  if (this.state === 'REJECTED') {
    // 这里应该放到微任务掉列里面,但无法实现,以同步执行代替
    rejected(this.reason)
  }
  if (this.state === 'PENDING') {
    this.taskResolveList.push(() => {
      fulfilled(this.value)
    })

    this.taskRejectList.push (() => {
      rejected(this.reason)
    })
  }
}

// 测试同步代码
new MyPromise((resolve, reject) => {
  resolve('打印同步代码')
}).then(res => {
  console.log(res)
})

// 测试异步代码
new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('1秒之后打印异步代码')
  }, 1000)
}).then(res => {
  console.log(res)
})

我们可以发现,这是一个发布订阅模式:收集依赖 -> 触发通知 -> 取出依赖执行。当然这是最基础的版本,并且无法模拟微任务,所以无法实现和真是 Promise 的一样的执行顺序。更加真实和详细的手写 Promise, 可以参考这篇文章

Promise api

Promise 原型还有很多其他api:

  1. Promise.prototype.catch()
  2. Promise.prototype.race()
  3. Promise.prototype.all()
  4. Promise.prototype.any()
  5. Promise.prototype.allSettled()
  6. Promise.prototype.finally()
  7. Promise.prototype.resolve()
  8. Promise.prototype.reject()

具体用法可以参考阮一峰老师的 ES6教程,讲得通俗易懂。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值