用生命谱写代码的赞歌

0%

JS 三种异步方式

异步与同步

  1. 异步

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    setTimeout(function (res) {
    console.log(res)
    },3000,"3秒后打印")
    setTimeout(function (res) {
    console.log(res)
    },2000,"2秒后打印")
    setTimeout(function (res) {
    console.log(res)
    },1000,"1秒后打印")
    // 结果
    // 1秒后打印
    // 2秒后打印
    // 3秒后打印
  2. 同步(使用回调将上面的异步变为同步)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function transSync() {
    setTimeout(function(res) {
    console.log(res)
    setTimeout(function(res) {
    console.log(res)
    setTimeout(function(res) {
    console.log(res)
    }, 1000, '1秒后打印')
    }, 2000, '2秒后打印')
    }, 3000, '3秒后打印')
    }
    transSync()
    // 结果
    // 3秒后打印
    // 2秒后打印
    // 1秒后打印
  3. 通过回调将异步变为同步存在一下几个问题

    • 回调层层嵌套,代码冗余
    • 回调太多,难以分辨哪个参数来源于哪个回调函数
    • 代码之间耦合度太高,一旦其中一个回调函数发生错误,其他回调函数也会遭殃
    • 无法用 try catch 来捕获错误

基于以上的问题的分析,异步解决方案 Promise 函数、 async 函数及 Generator 函数应运而生。

Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let promise = new Promise((resolve, reject) => {
setTimeout(res => {
resolve(res)
}, 3000, '3秒后打印')
})

promise.then(res => {
console.log(res)
return new Promise(resolve => {
setTimeout(res => {
resolve(res)
}, 2000, '2秒后打印')
})
}).then(res => {
console.log(res)
return new Promise(resolve => {
setTimeout(res => {
resolve(res)
}, 1000, '1秒后打印')
})
}).then(res => {
console.log(res)
})

Promise 优点是代码看起来清晰,每一个 then 里面的参数都是上一个异步返回 resolve 的结果对象。缺点是所有的回调都放在 then 里面,没有语义性。

Generator

  1. 用法

    Generator 函数有两个明显特征,一是 function 与函数名之间有 * 号,二是在函数内部使用 yield 表达式,定义不同的内部状态。 yield 表示中止执行,而 next 方法表示开启执行,返回的状态值为 yield 后面的表达式结果。Generator 函数返回一个遍历器对象。

  2. 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function* fn() {
    yield '1'
    yield '2'
    return 'end'
    }
    var res = fn()
    console.log(res.next()) // {value: "1", done: false}
    console.log(res.next()) // {value: "2", done: false}
    console.log(res.next()) // {value: "end", done: true}
    console.log(res.next()) // {value: undefined, done: true}
  3. 异步变为同步

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    function* gen() {
    yield new Promise(resolve => {
    setTimeout(res => {
    resolve(res)
    }, 3000, '3秒后打印')
    })
    yield new Promise(resolve => {
    setTimeout(res => {
    resolve(res)
    }, 2000, '2秒后打印')
    })
    yield new Promise(resolve => {
    setTimeout(res => {
    resolve('1秒后打印') // res 如果不传入 setTimeout 第三个参数则为 undefined
    }, 3000)
    })
    }

    let g = gen()
    g.next().value.then(res => {
    console.log(res)
    }).then(res => {
    g.next().value.then(res => {
    console.log(res)
    })
    }).then(res => {
    g.next().value.then(res => {
    console.log(res)
    })
    })

async

  1. 用法

    async 函数 是 Generator 函数的语法糖,它只是将 Generator 函数中的 * 变成了 async ,yield 变成了 await ,更具有语义性。
    async 函数直接执行,不需要通过 next
    async 函数返回的是一个 promise 对象,可以使用 then 方法添加回调函数。

  2. 示例

    1
    2
    3
    4
    5
    6
    async function fn() {
    return 'abc'
    }
    fn().then(res => {
    console.log(res) // abc
    })
  3. 异步变同步

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    async function foo() {
    await new Promise(resolve => {
    setTimeout(res => {
    console.log(res)
    resolve(res)
    }, 3000, '3秒后打印')
    })

    await new Promise(resolve => {
    setTimeout(res => {
    console.log(res) // res 应为 setTimeout 第三个参数
    resolve('2秒后打印')
    }, 2000)
    })

    await new Promise(resolve => {
    setTimeout(res => {
    console.log(res)
    resolve(res)
    }, 1000, '1秒后打印')
    })
    }

    foo()

参考文章