Skip to content

第 12 题:JS 异步解决方案的发展历程以及优缺点。 #11

@xiaofengqqcom123

Description

@xiaofengqqcom123

Activity

sisterAn

sisterAn commented on Feb 16, 2019

@sisterAn
Collaborator

JS 异步已经告一段落了,这里来一波小总结

1. 回调函数(callback)

setTimeout(() => {
    // callback 函数体
}, 1000)

缺点:回调地狱,不能用 try catch 捕获错误,不能 return

回调地狱的根本问题在于:

  • 缺乏顺序性: 回调地狱导致的调试困难,和大脑的思维方式不符
  • 嵌套函数存在耦合性,一旦有所改动,就会牵一发而动全身,即(控制反转
  • 嵌套函数过多的多话,很难处理错误
ajax('XXX1', () => {
    // callback 函数体
    ajax('XXX2', () => {
        // callback 函数体
        ajax('XXX3', () => {
            // callback 函数体
        })
    })
})

优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。)

2. Promise

Promise就是为了解决callback的问题而产生的。

Promise 实现了链式调用,也就是说每次 then 后返回的都是一个全新 Promise,如果我们在 then 中 return ,return 的结果会被 Promise.resolve() 包装

优点:解决了回调地狱的问题

ajax('XXX1')
  .then(res => {
      // 操作逻辑
      return ajax('XXX2')
  }).then(res => {
      // 操作逻辑
      return ajax('XXX3')
  }).then(res => {
      // 操作逻辑
  })

缺点:无法取消 Promise ,错误需要通过回调函数来捕获

3. Generator

特点:可以控制函数的执行,可以配合 co 函数库使用

function *fetch() {
    yield ajax('XXX1', () => {})
    yield ajax('XXX2', () => {})
    yield ajax('XXX3', () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()

4. Async/await

async、await 是异步的终极解决方案

优点是:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题

缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。

async function test() {
  // 以下代码没有依赖性的话,完全可以使用 Promise.all 的方式
  // 如果有依赖性的话,其实就是解决回调地狱的例子了
  await fetch('XXX1')
  await fetch('XXX2')
  await fetch('XXX3')
}

下面来看一个使用 await 的例子:

let a = 0
let b = async () => {
  a = a + await 10
  console.log('2', a) // -> '2' 10
}
b()
a++
console.log('1', a) // -> '1' 1

对于以上代码你可能会有疑惑,让我来解释下原因

  • 首先函数 b 先执行,在执行到 await 10 之前变量 a 还是 0,因为 await 内部实现了 generatorgenerator 会保留堆栈中东西,所以这时候 a = 0 被保存了下来
  • 因为 await 是异步操作,后来的表达式不返回 Promise 的话,就会包装成 Promise.reslove(返回值),然后会去执行函数外的同步代码
  • 同步代码执行完毕后开始执行异步代码,将保存下来的值拿出来使用,这时候 a = 0 + 10

上述解释中提到了 await 内部实现了 generator,其实 await 就是 generator 加上 Promise的语法糖,且内部实现了自动执行 generator。如果你熟悉 co 的话,其实自己就可以实现这样的语法糖。

本文首发于我的博客:JS异步解决方案的发展历程以及优缺点

inJs

inJs commented on Mar 7, 2019

@inJs

@sisterAn 没有依赖关系的异步操作不使用 await 就没有你所说的性能问题了。 比如:

async function test() {
  // 以下代码没有依赖性的话,不使用 await 便不会阻塞运行
  fetch('XXX1')
  fetch('XXX2')
  fetch('XXX3')
}
Zousdie

Zousdie commented on Mar 7, 2019

@Zousdie

@sisterAn 规范里好像没提到 await 内部实现了 generator, 如果从 polyfill 中的实现去断定 await 内部就是 generator……这样好像有点不严谨……

另外 await 的例子其实可以转换为

var a = 0
var b = () => {
  var temp = a;
  Promise.resolve(10)
    .then((r) => {
      a = temp + r;
    })
    .then(() => {
      console.log('2', a)
    })
}
b()
a++
console.log('1', a)
duanzheng

duanzheng commented on Mar 27, 2019

@duanzheng

@sisterAn 规范里好像没提到 await 内部实现了 generator, 如果从 polyfill 中的实现去断定 await 内部就是 generator……这样好像有点不严谨……

另外 await 的例子其实可以转换为

var a = 0
var b = () => {
  var temp = a;
  Promise.resolve(10)
    .then((r) => {
      a = temp + r;
    })
    .then(() => {
      console.log('2', a)
    })
}
b()
a++
console.log('1', a)

请教一下,为何 b 里不是直接访问 a?

changed the title [-]第十二题[/-] [+]第 12 题:JS 异步解决方案的发展历程以及优缺点。[/+] on Apr 26, 2019
Randysheng

Randysheng commented on May 23, 2019

@Randysheng

@duanzheng
若 b 里直接访问 a ,即写为 a = a + r,则最后then打印'a' 11,因为会先执行 a++。我猜他应该是为了保持最后的打印结果是 'a' 10,所以才使用了一个变量缓存最初a的值吧。个人见解

michaelcai

michaelcai commented on Jul 10, 2019

@michaelcai

@sisterAn 没有依赖关系的异步操作不使用 await 就没有你所说的性能问题了。 比如:

async function test() {
  // 以下代码没有依赖性的话,不使用 await 便不会阻塞运行
  fetch('XXX1')
  fetch('XXX2')
  fetch('XXX3')
}

其实提到了 Promise.al 就说明这里有个隐含前提,尽管三个 fetch 之前没有依赖,但是需要等待三个 fetch 都已经完成了再执行下一句。

arixse

arixse commented on Jul 10, 2019

@arixse

callback->Promise->generator->async/await

NuoHui

NuoHui commented on Jul 22, 2019

@NuoHui

需要好好理解的是async, await VS Generator改进了什么

  1. async内置执行器
  2. 更好的语义化
lk3407105

lk3407105 commented on Jul 23, 2019

@lk3407105

Promise 如果在这样写
Promise.resolve().then(function() { return new Promise(function() {}) })
这样写的话Promise就可以中止Promise执行链,相当于取消Promise了

252860883

252860883 commented on Aug 21, 2019

@252860883

@sisterAn 规范里好像没提到 await 内部实现了 generator, 如果从 polyfill 中的实现去断定 await 内部就是 generator……这样好像有点不严谨……

另外 await 的例子其实可以转换为

var a = 0
var b = () => {
  var temp = a;
  Promise.resolve(10)
    .then((r) => {
      a = temp + r;
    })
    .then(() => {
      console.log('2', a)
    })
}
b()
a++
console.log('1', a)

我尝试以下代码片段,按照您这样模拟是行不通的,不太理解为什么我这样写async里面输出就是 11:

let a = 0
let b = async () => {
  let c = await 10
  a = a + c
  console.log('2', a) // 2 11
}
b()
a++
console.log('1', a) // 1 1 
eightHundreds

eightHundreds commented on Aug 22, 2019

@eightHundreds

@sisterAn 规范里好像没提到 await 内部实现了 generator, 如果从 polyfill 中的实现去断定 await 内部就是 generator……这样好像有点不严谨……
另外 await 的例子其实可以转换为

var a = 0
var b = () => {
  var temp = a;
  Promise.resolve(10)
    .then((r) => {
      a = temp + r;
    })
    .then(() => {
      console.log('2', a)
    })
}
b()
a++
console.log('1', a)

我尝试以下代码片段,按照您这样模拟是行不通的,不太理解为什么我这样写async里面输出就是 11:

let a = 0
let b = async () => {
  let c = await 10
  a = a + c
  console.log('2', a) // 2 11
}
b()
a++
console.log('1', a) // 1 1 
let a = 0
let b = async () => {
  let tmp = a
  let c = await 10
  a = tmp + c
  console.log('2', a) // 2 10
}
b()
a++
console.log('1', a) // 1 1
kt3721

kt3721 commented on Sep 4, 2019

@kt3721

@sisterAn 规范里好像没提到 await 内部实现了 generator, 如果从 polyfill 中的实现去断定 await 内部就是 generator……这样好像有点不严谨……
另外 await 的例子其实可以转换为

var a = 0
var b = () => {
  var temp = a;
  Promise.resolve(10)
    .then((r) => {
      a = temp + r;
    })
    .then(() => {
      console.log('2', a)
    })
}
b()
a++
console.log('1', a)

我尝试以下代码片段,按照您这样模拟是行不通的,不太理解为什么我这样写async里面输出就是 11:

let a = 0
let b = async () => {
  let c = await 10
  a = a + c
  console.log('2', a) // 2 11
}
b()
a++
console.log('1', a) // 1 1 

上面说了执行到 await 的时候会保留 堆栈中的东西,这个时候变量a并没有使用,所以并没有保留 a = 0;当 await 结束后,再使用变量a,此时a的值经过 a++ 已经变成了 1 了。所以最后输出的是11。

24 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @duanzheng@dunhuang@michaelcai@lk3407105@yinzuowen

        Issue actions

          第 12 题:JS 异步解决方案的发展历程以及优缺点。 · Issue #11 · Advanced-Frontend/Daily-Interview-Question