【个人向整理】Promise


前言

网上关于Promise的文章确实是非常多了,但是自己实践的并不多,这里是针对自己的一个知识点小结和梳理,当然啦如果有错误欢迎提出^_^。

初定义

定义:Promise对象用于一个异步操作的最终完成/失败及其结果值的表示。
使用原因:避免回调嵌套层次过多。
拥有状态:

  • pending:初始/未定状态,初始化Promise时,调用executor函数后的状态。
  • fulfilled:成功状态。
  • rejected:失败状态。

状态转化:

  • pending -> fulfilled:操作成功
  • pending -> rejected:操作失败

状态转化是单向的,不可逆转。

最基本用法:
可以看到创建一个Promise实例,传入的参数是一个函数,这个函数称为executor/执行器。

new Promise((resolve, reject) => {
    if (success) {
        resolve(a) // pending to resolved
    } else {
        reject(err) // pending to rejectd
    }
})

方法

而Promise对象本身,有一些方法:
【个人向整理】Promise

  • race()
  • reject()
  • resolve()
  • all()

查看Promise的原型,发现它内置有几个方法:
【个人向整理】Promise

  • catch()
  • finally()
  • then()

Promise.prototype.then()

  • 参数:处理成功的函数,处理错误的函数
  • 返回值:返回一个Promise对象,所以可以链式调用。
promise.then(
    () => { console.log('我是成功后被执行的') },
    () => { console.log('我是失败后被执行的') })

Promise.prototype.catch()

  • 参数:捕捉的错误/reject()传来的参数
  • 返回值:返回一个Promise对象,所以可以链式调用。
  • Promise和then()中抛出错误能够不断传递,就能够在下一个catch()中统一处理,所以一般省略then中的第二个失败执行的函数。
promise.then(
    () => { console.log('我是成功后被执行的') }
).catch(
    (err) => { console.log(err) })

使用rejects()方法改变状态和抛出错误 throw new Error() 的作用是相同的

Promise.all()

  • 参数:可迭代参数,如:数组。
  • 用途:处理一些并发的异步操作,需要保证每个都执行完毕。
  • 结果:状态全为fulfilled->fulfilled,否则->rejected。

Promise.race()

  • 参数:可迭代参数,如:数组。
  • 用途:处理一些并发的异步操作,只需要其中一个执行完毕。
  • 结果:所有异步操作中有一个状态先改变,就采纳那个最先改变的状态为结果。

Promise.resolve()

  • 参数:普通值、Promise对象、带有then的对象。
  • 结果:一般情况返回一个状态为fulfilled的Promise对象。解析发生错误则返回rejected的Promise对象。
Promise.resolve('success')
// 其中[[PromiseStatus]]:"resolved"

Promise.reject('fail') 
// 其中[[PromiseStatus]]:"rejected"

Promise.resolve(Promise.reject('fail'))
// 其中[[PromiseStatus]]:"rejected"

由这个例子可以看出浏览器认为resolvedfulfilled是等价的,但Promise.resolve() 不一定让promise最终是fulfilled。所以对于resolved本身和fulfilled的区别,可以理解为resolved等价于compiled,即可能是成功也可能是失败。

Promise.reject()

  • 参数: 发生异常的原因。
  • 结果:返回一个rejected状态的Promise对象。

注意点

状态变化

  • Promise状态只会改变一次。
  • 构造函数中的resolve()/reject()只有第一次执行有效,多次调用没有作用。
  • Promise状态改变,并且传递了一个值,后续调用.then()/.catch()都可直接拿到该值。

参数/返回值

  • .then()/.catch()的参数应该是函数,传入非函数则会发生值穿透。
Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)  //1
  • .then()/.catch()不能返回Promise本身,会造成死循环。
  • .then()/.catch()中return一个error对象并不会抛出错误,所以无法捕捉。

因为返回任意一个非Promise 的值都会被包裹成Promise对象,即 return new Error(‘error!!!’)等价于return Promise.resolve(new Error(‘error!!!’))

执行顺序

  • Promise构造函数是同步执行的,resolve()/reject()后的代码也会执行。Promise.then()中的函数是异步执行的。**

以下输出:1243

const promise = new Promise((resolve, reject) => {
  console.log(1)
  resolve()
  console.log(2)
})
promise.then(() => {
  console.log(3)
})
console.log(4)
  • process.nextTickpromise.then属于microtasksetImmediate属于 macrotask。在每一次事件循环中,macrotask只会提取一个执行,而microtask会一直提取,直到microsoft队列为空为止。

以下输出:end nextTick then setTimeout1 setTimeout2

process.nextTick(() => {
  console.log('nextTick')
})

setTimeout(() => {
  console.log('setTimeout1')
})

Promise.resolve()
  .then(() => {
    console.log('then')
  })
  
setTimeout(() => {
  console.log('setTimeout2')
})

console.log('end')

补充:
macrotasks:

  • setTimeout
  • setInterval
  • setImmediate
  • requestAnimationFrame
  • I/O
  • UI rendering

microtasks:

  • process.nextTick
  • Promises
  • Object.observe
  • MutationObserver

参考文章

ES6关于Promise的用法
Promise 必知必会(十道题)
javascript中的异步 macrotask 和 microtask 简介
Tasks, microtasks, queues and schedules
Difference between microtask and macrotask within an event loop context


发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>