async await 介绍 从0手动实现async await

1 async await介绍

        async 和 await 是用于处理异步编程的语法糖,它们简化了异步操作的编写,使其看起来像同步代码。通过 async 标记一个函数为异步函数,返回的是一个 Promise 对象,而 await 用来暂停执行,直到 Promise 解决9相当于then函数),错误处理使用try...catch...(相当于catch),从而实现异步流程的同步化表达。下面将通过代码示例逐步解释上面一段话。

1.1  async 标记函数 返回 Promise 对象

        调用async方程式将会返回Promise对象,即使返回值不是Promise也会包装,相当于调用了Promise上的静态类方法resolve对返回值进行了封装,代码如下:

async function asyncFunction() {
    console.log("inside asyncFunction");
    return 'Hello';
}

async function undefinedFunction(){
    console.log("inside undefinedFunction");
    console.log('no return value');
}

// 调用async函数时会立即执行函数内部的代码,并且将返回一个promise对象
console.log(asyncFunction()); // Promise { 'Hello' }  // async function always return a promise
// 相当于resolve了一个promise
Promise.resolve('Hello')
console.log(undefinedFunction()); // Promise { undefined }
// 输出结果:
// inside asyncFunction
// Promise { 'Hello' }
// inside undefinedFunction
// Promise { undefined }

1.2 await 用来暂停执行,直到 Promise 解决 错误处理

        await 的工作原理类似于调用 Promise 的 then 方法,等待 Promise 从 pending 状态变为 fulfilled 状态后返回结果(相当于暂停代码了,后面都是回调函数callback)。如果 Promise 失败或一直处于 pending 状态,将会在下文介绍。因为 async 函数返回的是一个 Promise,所以 await 后面不仅可以跟 Promise,也可以跟 async 函数或其他值。若是其他值,JavaScript 会先将其转换为 Promise.resolve(val),再继续执行。这样 await 可以兼容不同类型的数据并处理异步操作。

        下面代码是一般的await的使用,演示了Promise,async function,normal value的处理情况。 

(async function awaitTestFunction() {
    // await 处理async函数返回的promise对象
    let res = await asyncFunction()
    console.log(res) // Hello

    // await 处理promise对象
    res = await new Promise((resolve) => {
        setTimeout(() => {
            resolve('world')
        }, 1000)
    })
    console.log(res) // world

    // await 处理普通值
    res = await 'normal value'
    console.log(res) // normal value
})();
// 最终输出结果
// Hello
// world
// normal value

        当出现Promise一直处于pending状态,throw error 或者rejected状态就会代码执行,这时就要使用try catch捕获错误(这样就替代了.catch的作用)如下所示:

async function pendingFunction(){
    console.log("inside pendingFunction");
    // await将会一直等待promise对象的状态变为resolved,在node环境中将会直接导致程序退出
    await new Promise(() => {
    })
    // 将会无法执行到这里
    console.log('after pending')
}

async function rejectFunction(){
    console.log("inside rejectFunction");
    try {
        // 抛出错误
        await new Promise((undefined, reject) => {
            // reject('reject error during new Promise instance')
            throw new Error('throw error')
        })
    } catch (error) {
        console.log(error) // reject
    }
    // 不使用try catch将会无法执行到这里
    console.log('after reject')
}

async function Testall(){
    // await awaitTestFunction()
    // await pendingFunction()
    await rejectFunction()
}

Testall()

        await一直处于pending状态将会导致程序直接中断。await后面的错误不使用try...catach捕获将会导致程序报错终止 。

1.3 async和await综合使用

        现在将上面所有的async函数按照从前到后的顺序依次执行,实现异步操作变为同步操作,代码如下所示:

async function asyncFunction() {
    console.log("inside asyncFunction");
    return 'Hello';
}

async function undefinedFunction(){
    console.log("inside undefinedFunction");
    console.log('no return value');
}

async function awaitTestFunction() {
    // await 处理async函数返回的promise对象
    let res = await asyncFunction()
    console.log(res) // Hello

    // await 处理promise对象
    res = await new Promise((resolve) => {
        setTimeout(() => {
            resolve('world')
        }, 1000)
    })
    console.log(res) // world

    // await 处理普通值
    res = await 'normal value'
    console.log(res) // normal value
}

async function pendingFunction(){
    console.log("inside pendingFunction");
    // await将会一直等待promise对象的状态变为resolved,在node环境中将会直接导致程序退出
    await new Promise(() => {
    })
    // 将会无法执行到这里
    console.log('after pending')
}

async function rejectFunction(){
    console.log("inside rejectFunction");
    try {
        // 抛出错误
        await new Promise((undefined, reject) => {
            // reject('reject error during new Promise instance')
            throw new Error('throw error')
        })
    } catch (error) {
        console.log(error) // reject
    }
    // 不使用try catch将会无法执行到这里
    console.log('after reject')
}

// 如果顶层异步函数之间资源有先后要求,可以使用await来保证执行顺序
async function Testall(){
    await asyncFunction()
    await undefinedFunction()
    await awaitTestFunction()
    // await pendingFunction()
    await rejectFunction()
}

Testall()

1.4 手写代码实现async和await逻辑

  •  模拟async关键字:实现一个函数,它返回一个Promise并且内部控制异步流程

  •  模拟await关键字:在函数内部能够暂停执行,等待一个异步操作完成再继续执行

asyncFunction(generatorFunc) 实现

        首先我们就实现一个通过接收生成器函数,然后生成async function的函数,返回值是一个函数(该函数返回值是Promise 和 async方程对应),函数内部实现执行器函数,控制生成器函数内部代码的执行,返回也是Promise,实现代码如下:

// async await手写实现
// 使用生成器函数yield暂停执行以及Promise的异步执行 相当于await
// 执行器函数自动处理生成器的迭代和Promise的解决,模拟了async await的执行过程

// 目标:模拟async关键字:实现一个函数,它返回一个Promise并且内部控制异步流程
// 目标:模拟await关键字:在函数内部能够暂停执行,等待一个异步操作完成再继续执行

// 传入一个生成器函数,返回相当于模拟async的函数
function asyncFunction(generatorFunc){
    return function(...args){
        // 获取生成器,用于控制异步流程同步执行
        const generator = generatorFunc.call(this, ...args)

        // 执行器函数,控制函数内部异步代码的执行,返回的时Promise
        function handleNext(next){
            // 递归调用结束条件,当执行完毕时,直接返回Promise
            if(next.done) return Promise.resolve(next.value)

            // 没有执行完毕时, 也是返回Promise
            // 1. 取出value 执行Promise等待结果
            // 2. 成功继续调用handleNext
            // 3. 失败由生成器抛出错误
            return Promise.resolve(next.value).then(res => {
                // next函数也可以传递参数,相当于上一个yield函数的返回值
                // const res = yield 1 相当于 generator().next(1)
                // 递归调用, 知道函数结束
                return handleNext(generator.next(res))
            }, err => {
                return handleNext(generator.throw(err))
            })
        }

        // 调用async方程式会自动调用handleNext
        try {
            return handleNext(generator.next())
        } catch (error) {
            return Promise.reject(error)
        }
    }
}
generatorFunc实现 

        接下来定义一个generator函数,该函数流程和实际的async方程一致(相当于async function -> function*, await -> yield),如下所示是generator函数:

// 定义一个generator方程
function* fetchDataGenerator(){
    console.log("start fetch")
    // yield 关键字模拟await
    const data = yield fetch("https://api.github.com/users/zhengyuanbo")
    console.log("start data.json()")
    const res = yield data.json()
    console.log("async func finish:")
    return res
}

        对应的async function的实现方式如下所示:

// async function 实现方式
async function fetchData(){
    console.log("start fetch")
    const data = await fetch("https://api.github.com/users/zhengyuanbo")
    console.log("start data.json()")
    const res = await data.json()
    console.log("async func finish:")
    return res
}
模拟async function的实现

        将生成器传图asyncFunction就可以得到模拟async方程的函数,如下所示:

// 传入fetchDataGeneerator生成最终的async function
const asyncfetchDataFunc = asyncFunction(fetchDataGenerator)

asyncfetchDataFunc().then(res => {
    console.log(".then callback func")
    console.log(res)
})
 手写实现过程的总结和注意事项
  • 在handleNext函数中处理未执行完成的任务时,then的回调函数中会递归调用handleNext(generator.next()),需要注意要把handleNext(generator.next()) return出去,否则会导致外层async function返回值提前调用then中的回调函数且无法获取返回值(为undefined)这破化了Promise链的完整性。
  • 生成器函数与 yield 对应 await: 使用生成器函数中的 yield 来暂停执行,等待异步操作完成,模拟 await 的行为。
  • 执行器函数返回一个 Promise: 确保执行器函数(如 asyncFunction)返回一个 Promise,以便外部可以通过 .then 和 .catch 处理结果。
  • 正确传递 next 的参数: 将每次 Promise 解决的结果通过 generator.next(res) 传回生成器,使得生成器内部能够获取异步操作的结果,继续执行后续代码。
  • 错误处理通过 generator.throw(err): 在 Promise 被拒绝时,通过 generator.throw(err) 将错误传回生成器,以便在生成器内部使用 try...catch 进行处理。

1.5 总结与注意事项

  •  不能在全局作用域顶层使用await,将异步操作封装在async函数中然后再调用。
  •  async和await与Promise是相辅相成的,async相当于new Promse,await相当于.then方法,使用try...catch...相当于.catch方法
  • async和await是语法糖,将异步执行的代码转变为同步执行(这是由于js单线程执行的特性)。
  • async 函数返回 Promise:当一个函数使用 async 关键字定义时,它会自动返回一个 Promise,即使函数内部没有显式返回 Promise。
  • await 等待异步操作完成:await 用于暂停 async 函数的执行,等待 Promise 解决(fulfilled),并返回其结果。
  • 兼容同步值:await 不仅可以处理 Promise,如果跟随的是非 Promise 值,会被自动包装为 Promise.resolve(val)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

It'sMyGo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值