JavaScript 异步编程

63 阅读2分钟

Promise

主要为了解决回调地狱问题,由 CommonJS 社区提供,它实际就是一个对象,用来表示一个异步任务结束过后究竟是成功还是失败,开始时是 Pending 状态,成功就是 Fulfilled,并执行 onFulfilled,失败就是 Rejected,且执行 onRejected

通过 Promise 就可以实现链式调用,执行 then 后会返回一个全新的 Promsie,每一个 then 都是在为上一个 then 返回的 Promise 对象添加状态明确过后的回调,都会依次执行

当然 then 里也可以主动返回 Promise

如果返回的是普通值,这个值会作为后面 then 回调的参数

p1.then(res => { return p2 })
  .then(res => {})
  .then(res => {})

这样通过链式调用就可以来减少嵌套,保证扁平化

嵌套使用是 Promise 常见误区

p1.then(res => {
p2.then(res => {})
})

我们通常也可以使用 catch 来代替 onRejected 回调,效果一致。

它相当于 then 的第一个参数为 undefined,更加适用于链式调用。

它们的区别主要是catch 不仅可以捕获到第一个 Promise 对象的错误,也可以捕获到前面的 then 方法的错误,而 onRejected 只能捕获到第一个 Promise 的错误

p1.then(onFulfilled, onRejected)
p1.then().catch()

Promise 上任何一个异常都会被向下传递,直到被捕获

常用静态方法

Promise.resolve()

如果参数是非 Promise,就会返回一个值为该参数的成功的 Promise

如果是 Promise 对象,则会原样返回,两者全等

它还有一个应用点是转化 thenable 对象,使其成为原生的 Promise

Promise.rejected 同理

并行执行

Promise.all

const promise = Promise.all([p1, p2])
​
promise.then(res => {}) // 拿到的是每个异步任务的结果数组
  .catch(err => {}) // 其中一个失败即失败

此外还有 Promise.race

Generator

生成器函数就是普通的函数加个 *

function *fn() {}

运行这个函数后不会立即执行,而是得到一个生成器对象

const generator = fn()

只有手动执行这个函数的 next 方法,它的函数体才会执行

generator.next()

同时,我们可以在函数体内随时使用一个 yield 关键字来向外返回一个值,并且可以在 next 方法中拿到这个值

// yield 'foo'
{ value: 'foo', done: false }

如果在 next 中传入一个参数,它会作为 yield 表达式的值,比如执行 next('bar')

const res = yield 'foo'
console.log(res) // 'bar'

如果在外部使用生成器抛出一个异常,如下

generator.throw(new Error('error'))

则会在 yield 表达式处捕获到这个异常

try {
  const res = yield 'foo'
} catch (e) {}

Async Await

async function fn() {
  try {
    const res = await ajax()
  } catch (e) {}
}
​
const p = fn.then()

接下来看几道题目

async function fn() {
  let a = await new Promsie((resolve) => {})
  console.log(a)
}
fn()
// 不会打印,它会等待 Promise resolve 之后的值