乐趣区

关于es6:这次10分钟就帮你搞定asyncawait原理

Javascript 虽博大精深,但如果只能同步运行,那么遇到 HTTP 网络申请、I/O 解决、事件、定时器等耗时操作就会卡到没法用。那么 js 是如何解决异步编程的?

回调函数

在 js 中经常将函数作为参数传递,当工作执行有了后果就调用这个函数来进行下一步操作。假如咱们须要拿到前一个申请的后果来进行下一个申请,一旦申请多起来了,代码会显得十分俊俏。

http.get("url", param, (err, res1) => {console.log("res1:", res1)
    http.get("url", res1, (err, res2) => {console.log("res2:", res2)
        http.get("url", res2, (err, res3) => {console.log("res3:", res3)
                        ...
                    })
                })
            })

从下面代码能够看出,仅仅嵌套三次曾经有堆砌 💩 山趋势了。。。

Promise

ES6 引入了 js 异步编程新计划 Promise,通过 Promise 的链式调用最起码让代码看起来整齐划一了不少,一旦代码堆砌起来还是影响浏览和保护

function queryData(url, param) {return new Promise((resolve, reject) => {http.get(url, param, (err, data) => {resolve(data)
        })
    })
}
queryData("/api/user", data)
    .then(res1 => queryData("/api/xx", res1))
    .then(res2 => queryData("/api/xx", res2))
    .then(res3 => queryData("/api/xx", res3))
    .then(finalRes => console.log("finalRes:", finalRes))
    .catch(error => console.log("error:", error))

async/await

最终咱们在 ES7 中迎来了青天大老爷——async\await,应用它们就能够从上到下逐行地编写咱们的异步代码,进而从视觉上达到同步成果,浏览起来相当难受,例如以上代码能够改写如下

async function request() {const res1 = await queryData("/api/user", data)
    const res2 = await queryData("/api/user", res1)
    const res3 = await queryData("/api/user", res2)
    const finalRes = await queryData("/api/user", res3)
    console.log("finalRes:", finalRes)
}

这样写霎时就清新了。tip: async/await 必须一起应用,否则会报错。那么它的原理是什么呢?首先咱们来理解一下协程的概念

协程

协程有点像线程,然而是一个更轻量级的存在,能够由咱们本人写程序来治理。它的步骤大略如下:

  1. 协程 A 开始执行
  2. 协程 A 执行到一半,进入暂停,将执行权转移给协程 B
  3. 一段时间后协程 B 交还执行权,协程 B 进入暂停
  4. 协程 A 复原执行

Genarator 函数

genarator 函数是 js 里协程的一种实现,它的申明形式如下:

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

函数名前须要加一个星号,函数体内须要有 yield 关键字。genarator 函数是一个生成器,调用后并不执行函数体,而是返回一个 iterator 对象,通过调用 iterator 对象上的 next 办法来执行 genarator 函数,首次调用执行到 yield 关键字就暂停,再次调用执行到下一个 yield 关键字,以此类推。



调用 iterator 对象的 next 办法会返回一个对象 {value: …, done: …},value 为每次执行到 yield 时前面跟的值,done 是一个布尔值,false 示意函数没有执行完前面还有 yield,true 示意执行结束,没有 yield 了。当 done 为 true 时,value 为函数的返回值。


调用 iterator 的 next 办法时还能够传参,参数能够在 yield 前面的赋值语句中进行赋值。如果不进行传参则会失落 yield 前面的值。



比照 async/await 与 genarator 函数会发现

  1. async 函数调用返回 promise,genarator 函数调用返回 iterator 对象
  2. async 函数无需通过手动调用的形式执行函数体,也就是说它自带执行器
  3. 用 async/await 代替 */yield 领有更好的语义性

那么咱们来炫一个,应用 genarator 函数和 Promise 实现咱们的 async 函数。

async function someFunction(args) {//...}

等同于:

function fn(num) {
    return new Promise(resolve => {setTimeout(() => {resolve(num * 2)
        }, 1000)
    })
}
function* gen(param) {const res1 = yield fn(param)
    console.log("res1-->", res1)
    const res2 = yield fn(res1)
    console.log("res2-->", res2)
    const res3 = yield fn(res2)
    console.log("res3-->", res3)
    return res3
}

function genToAsync(gen) {return function () {const g = gen.apply(this, arguments)
        return new Promise((resolve, reject) => {function run(val) {
                let result
                try {result = g.next(val)
                } catch (err) {return reject(err)
                }
                const {value, done} = result
                if (done) {resolve(value)
                    return
                }
                return Promise.resolve(value)
                    .then(res => {run(res)
                    })
                    .catch(err => {reject(err)
                    })
            }
            run()})
    }
}
const asyncGen = genToAsync(gen)
asyncGen(2).then(res => console.log("res-->", res))

执行后果

由以上代码咱们能够看到实现了和 async/await 一样的成果

退出移动版