# ❓JS 异步解决方案的发展历程以及优缺点

# 回调函数形式(callback)

ajax('', () => {
  // callback
  ajax('', () => {
    // callback
    ajax('', () => {
      // ...
    })
  })
})
1
2
3
4
5
6
7
8
9

# 优点

  • 解决同步问题

# 缺点

  • 容易导致回调地狱
  • 嵌套函数存在耦合性,一旦有所改动,就会牵一发而动全身,即(控制反转)
  • 嵌套函数过多的多话,很难处理错误

# Promise 形式

  • pending 初始状态
  • fulfilled 操作成功
  • rejected 操作失败
new Promise((resolve, reject) => {
  // resolve()
  // or
  // reject()
})
  .then(() => {})
  .catch(() => {})
1
2
3
4
5
6
7

# 优点

  • 可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
  • Promise 对象提供统一的的接口,使得控制异步操作更加容易

# 缺点

  • 无法取消,一旦新建它就会立即执行,无法中途取消
  • 如果不设置回调函数,Promise 内部抛出的错误,不会反映到外部
  • 当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚开始还是即将完成)

# Generator

function* helloWorldGenerator() {
  yield 'hello'
  yield 'world'
  return 'ending'
}

var hw = helloWorldGenerator()

hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true } 以后再次调用还是这个值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 优点

  • 异步操作同步化表达,改写回调函数
  • 控制流管理,可以改善大量的 promise 语法
  • 部署 Iterator 接口,利用 Generator 函数,可以在任意对象上部署 Iterator 接口。
  • 作为数据结构,更确切地说,可以看作是一个数组结构,因为 Generator 函数可以返回一系列的值,这意味着它可以对任意表达式,提供类似数组的接口。

# 缺点

  • 没有内置执行器,需要手动执行
  • 语义化不清楚,不容易阅读理解

# Async/Await

就是 Generator 函数的语法糖,使得异步操作变得更加方便。

const asyncReadFile = async function() {
  const f1 = await readFile('/etc/fstab')
  const f2 = await readFile('/etc/shells')
  console.log(f1.toString())
  console.log(f2.toString())
}
1
2
3
4
5
6

# 优点

  • 内置执行器
  • 更好的语义,async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果
  • 更广的适用性,await 命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)
  • 返回值是 Promise, 这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用 then 方法指定下一步的操作