Skip to content

异步编程

  • 回调函数 的方式,使用回调函数的方式有一个缺点是,多个回调函数嵌套的时候会造成回调函数地狱,上下两层的回调函数间的代码耦合度太高,不利于代码的可维护。
  • Promise 的方式,使用 Promise 的方式可以将嵌套的回调函数作为链式调用。但是使用这种方法,有时会造成多个 then 的链式调用,可能会造成代码的语义不够明确。
  • generator 的方式,它可以在函数的执行过程中,将函数的执行权转移出去,在函数外部还可以将执行权转移回来。当遇到异步函数执行的时候,将函数执行权转移出去,当异步函数执行完毕时再将执行权给转移回来。因此在 generator 内部对于异步操作的方式,可以以同步的顺序来书写。使用这种方式需要考虑的问题是何时将函数的控制权转移回来,因此需要有一个自动执行 generator 的机制,比如说 co 模块等方式来实现 generator 的自动执行。
  • async 函数 的方式,async 函数是 generatorpromise 实现的一个自动执行的语法糖,它内部自带执行器,当函数内部执行到一个 await 语句的时候,如果语句返回一个 promise 对象,那么函数将会等待 promise 对象的状态变为 resolve 后再继续向下执行。因此可以将异步逻辑,转化为同步的顺序来书写,并且这个函数可以自动执行。

Promise

Promise 是异步编程的一种解决方案,解决异步编程回调函数造成的回调地狱问题;

三种状态

  • pending
  • fulfilled
  • rejected

状态转换

  • pending -> fulfilled
  • pending -> rejected

注:pending 状态一旦被转换就不再更改。

常用方法

  • then 方法,接收两个回调函数作为参数,第一个 fulfilled 时调用,第二个 rejected 时调用;
  • catch 方法,相当于 then 方法的第二个参数,通常用于兜底捕获异常;
  • finally 方法,不管最后状态如何都会执行;
  • all 方法,当所有 promise 状态都为 fulfilled 时才为 fulfilled,如果有一个为 rejected 状态就为 rejected
  • race 方法,当首个 promise 状态发生改变时,状态与这个 promise 一致;

应用场景

  • 回调函数改造,支持 promise 写法,例如:XMLHttpRequest;
js
function request(options) {
  return Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.onreadystatechange = function () {
      xhr.readyState == 4 && xhr.status == 200
        ? resolve(xhr.responseText)
        : reject()
    }
    xhr.open(options.method, options.url, true)
    xhr.send(options.data)
  })
}
  • 创建竞态条件,以保证 Promise 始终有输出信号,例如:超时操作,未在指定时间内返回;
js
const timeout = new Promise((resolve, reject) =>
  setTimeout(reject, 10 * 1000)
)
const fetchList = fetch('xxx.xxx.xxx/xxx/xxx')

Promise.race([fetchList(), timeout()])
  .then(() => {
    console.log('请求成功')
  })
  .catch(() => {
    console.log('请求失败或超时')
  })

generator

生成器函数,可手动控制执行进度,并且其执行结果是一个迭代器,即可使用 for of 遍历。

js
function* gen() {
  yield 1
  yield 1 + 1
  return 3
}

const g = gen()
g.next() // { value: 1, done: false }
g.next() // { value: 2, done: false }
g.next() // { value: 3, done: true }

for (const value of gen()) {
  console.log(value)
}
// 1
// 2

next 可传一个参数,该参数作为上一个 yield 表达式的值赋给变量。

js
function* gen(a) {
  const b = yield a + 1
  const c = yield b + 2
  return a + b + c
}

const g = gen(10)
g.next() // { value: 11, done: false }
g.next(-5) // { value: -3, done: false } b = -5
g.next(-1) // { value: 4, done: false } c = -1

异常处理,throw 方法可抛出异常并提前结束。

js
function* gen() {
  try {
    yield 1
    yield 2
    return
  } catch (error) {
    console.error(error) // 出错咯
  }
}
const g = gen()
g.next() // { value: 1, done: false }
g.throw('出错咯!')
g.next() // { value: undefined, done: true }

return 方法可提前结束;

js
function* gen() {
  yield 1
  yield 2
  return 3
}

const g = gen()
g.next() // { value: 1, done: false }
g.return() // { value: undefined, done: true }
g.next() // { value: undefined, done: true }

应用场景

  • 使用迭代器遍历链表。
js
const d = { value: 'd', next: null }
const c = { value: 'c', next: d }
const b = { value: 'b', next: c }
const a = { value: 'a', next: b }
const header = a

function* iterator(link) {
  while (link) {
    yield link.value
    link = link.next
  }
}

for (const value of iterator(header)) {
  console.log(value)
}
// a
// b
// c
// d

async/await

async/await 其实是 Generator 的语法糖,自带执行器的 Generator,并且返回 Promise。解决了 Promise 顺序执行 then 方法链式调用过长问题。

js
function* gen() {
  let result
  result = yield 1
  console.log(result) // 1
  result = yield 2
  console.log(result) // 2
  result = yield 3
  console.log(result) // 3
  result = yield 4
  console.log(result) // 4
  result = yield 5
  console.log(result) // 5
  result = yield 6
  console.log(result) // 6
  return 7
}

function runGenerator(generatorFunction, ...args) {
  const generator = generatorFunction(...args)
  const next = (...args) => {
    const nextValue = generator.next(...args)
    if (nextValue.done) return Promise.resolve(nextValue.value)
    return Promise.resolve(nextValue.value).then(next)
  }
  return next()
}

runGenerator(gen).then(res => {
  console.log(res) // 7
})