JavaScript异步编程中async函数详解

作者:小括号

时间:2026-10-25

浏览:116

本文深入解析JavaScript中async函数的核心机制,阐述其作为Generator语法糖的实现原理。详细讲解async返回值特性、await表达式的执行逻辑及错误处理,并通过文件读取与AJAX请求示例演示实际应用。最后对比async与Generator在语义、执行器及返回值上的差异,帮助开发者掌握现代异步编程最佳实践。

文章目录

async函数

async函数始终返回一个Promise对象,该Promise的最终结果取决于async函数内部执行的返回值。引入async的主要目的是简化异步操作的编写,从语法层面来看,它本质上是Generator函数的语法糖。

定义async函数时,即使函数内部返回的不是Promise对象,调用该函数后得到的结果依然是一个Promise对象。具体实现代码如下:

当返回结果不是Promise对象时的情况:

<script>
    async function fn(){
        // 返回的结果是字符串
        // return '123'
        // // 返回的结果是undefined
        // return;
        // 返回的结果是抛出一个异常
        throw new 'error'
    }
    const result = fn()
    console.log(result);
</script>

async函数

若返回结果是Promise对象,则可直接使用then方法进行链式调用,示例如下:

<script>
    async function fn(){
        return new Promise((resolve,reject)=>{
            // resolve('成功的数据')
            reject('失败的数据')
        })
    }
    const result = fn()
    // 调用 then 方法
    result.then((value)=>{
        console.log(value);
    },(reason)=>{
        console.log(reason); // 打印失败的数据
    })
</script>

await 表达式

仅看async函数似乎功能有限,但实际上必须配合await使用才能发挥其作为语法糖的优势。

await具有以下核心特点:

await关键字必须放置在async函数内部使用。

await右侧的表达式通常应为Promise对象。

await会返回Promise对象成功(resolve)时的值。

如果await关联的Promise对象进入失败(reject)状态,则会抛出异常,此时必须通过try...catch语句进行捕获和处理。

通俗理解:await的作用类似于then方法的第一个回调函数,仅获取成功值;若发生失败,则需借助try...catch捕获错误。

当async函数内部抛出错误时,其返回的Promise对象将变为reject状态。抛出的错误对象会被Promise的catch方法回调函数接收。

<script>
    const p = new Promise((resolve,reject)=>{
        // resolve('用户数据')
        reject('用户加载数据失败了')
    })
    async function fn(){
        // 为防止promise是失败的状态,加上try...catch进行异常捕获
        try {
            // await 返回的结果就是 promise 返回成功的值
            let result = await p
            console.log(result);
        } catch (error) {
            console.log(error);//因为是失败的状态,所以打印:用户加载数据失败了
        }
    }
    fn()
</script>

总结:

(1)await命令后的Promise对象可能处于rejected状态,因此建议将await命令置于try...catch代码块中。

(2)若多个await命令后的异步操作之间无继发依赖关系,应尽可能让它们同时触发。

例如:使用await Promise.all([a(), b()]),此处仅作简要提及。

(3)await命令仅允许在async函数中使用,若置于普通函数中将会导致报错。

(4)理解async的运行原理:async函数能够保留运行堆栈。普通函数内部执行异步任务时,若异步任务结束,普通函数可能早已执行完毕,导致异步任务的上下文环境消失;若异步任务报错,错误堆栈将不包含普通函数。而async函数内部的异步任务运行时,async函数本身会暂停执行,因此一旦内部异步任务报错,错误堆栈将包含async函数。

async使用形式

// 函数声明
async function foo() {}
// 函数表达式
const foo = async function () {};
// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)
// Class 的方法
class Storage {
  constructor() {
    this.cachePromise = caches.open('avatars');
  }
  async getAvatar(name) {
    const cache = await this.cachePromise;
    return cache.match(`/avatars/${name}.jpg`);
  }
}
const storage = new Storage();
storage.getAvatar('jake').then(…);
// 箭头函数
const foo = async () => {};

async读取文件

与之前介绍的Promise读取文件内容类似,我们同样可以使用async进行文件读取,代码如下:

// 1.引入 fs 模块
const fs = require('fs')
// 2.读取文件
function index(){
    return new Promise((resolve,reject)=>{
        fs.readFile('./index.md',(err,data)=>{
            // 如果失败
            if(err) reject(err)
            // 如果成功
            resolve(data)
        })
    })
}
function index1(){
    return new Promise((resolve,reject)=>{
        fs.readFile('./index1.md',(err,data)=>{
            // 如果失败
            if(err) reject(err)
            // 如果成功
            resolve(data)
        })
    })
}
function index2(){
    return new Promise((resolve,reject)=>{
        fs.readFile('./index2.md',(err,data)=>{
            // 如果失败
            if(err) reject(err)
            // 如果成功
            resolve(data)
        })
    })
}
// 3.声明一个 async 函数
async function fn(){
    let i = await index()
    let i1 = await index1()
    let i2 = await index2()
    console.log(i.toString());
    console.log(i1.toString());
    console.log(i2.toString());
}
fn()

async函数

async发送AJAX请求

与之前讲解的Promise发送AJAX请求方式一致,我们也可使用async发送AJAX请求,代码如下:

<script>
    // 发送 AJAX请求,返回的结果是 Promise 对象
    function sendAjax(url){
        return new Promise((resolve,reject)=>{
            // 创建对象
            const x = new XMLHttpRequest()
            // 初始化
            x.open('GET',url)
            // 发送
            x.send()
            // 事件绑定
            x.onreadystatechange = function(){
                if(x.readyState === 4){
                    if(x.status >= 200 && x.status < 300){
                        // 如果响应成功
                        resolve(x.response)
                        // 如果响应失败
                        reject(x.status)
                    }
                }
            }
        })
    }
    // promise then 方法测试
    // const result = sendAjax("https://ai.baidu.com/").then(value=>{
    //     console.log(value);
    // },reason=>{})
    // async 与 await 测试
    async function fn(){
        // 发送 AJAX 请求
        let result = await sendAjax("https://ai.baidu.com/")
        console.log(result);
    }
    fn()
</script>

async函数

与生成器(Generator)相比

对比发现,async与await的关系类似于Generator与yield的关系。不熟悉Generator的读者可参考往期文章:生成器讲解。比较可知:async函数是将Generator函数的星号(*)替换为async,将yield替换为await。代码对比如下:

<script>
    // Generator 函数
    function * person() {
        console.log('hello world');
        yield '第一分隔线' 
        console.log('hello world 1');
        yield '第二分隔线'
        console.log('hello world 2');
        yield '第三分隔线'
    }
    let iterator = person()
    // console.log(iterator); 打印的就是一个迭代器对象,里面有一个 next() 方法,我们借助next方法让它运行
    iterator.next()
    iterator.next()
    iterator.next()
    // async函数
    const person1 = async function (){
        console.log('hello world');
        await '第一分隔线' 
        console.log('hello world 1');
        await '第二分隔线'
        console.log('hello world 2');
        await '第三分隔线'
    }
    person1()
</script>

async函数

async函数的实现原理是将Generator函数与自动执行器封装在一个函数中。

<script>
    async function fn(args) {}
    // 等同于
    function fn(args) {
        // spawn函数就是自动执行器
        return spawn(function* () {});
    }
</script>

我们可以分析Generator和async代码的书写特点与风格:

<script>
    // Generator 函数
    function Generator(a, b) {
        return spawn(function*() {
            let r = null;
            try {
                for(let k of b) {
                r = yield k(a);
                }
            } catch(e) {
                /* 忽略错误,继续执行 */
            }
            return r;
        });
    }
    // async 函数
    async function async(a, b) {
        let r = null;
        try {
            for(let k of b) {
            r = await k(a);
            }
        } catch(e) {
         /* 忽略错误,继续执行 */
        }
        return r;
    }
</script>

因此,async函数的实现符合语义且更为简洁,无需编写Generator的自动执行器,而是由语言底层提供支持,从而减少了代码量。

基于上述代码,可总结出以下几点:

(1)Generator函数执行需借助外部执行器,而async函数内置执行器,即async无需像生成器那样依赖next方法即可执行,而是自动运行。

(2)相较于生成器函数,async函数的语义更加清晰直观。

(3)如前所述,async函数可接受Promise对象或其他原始类型,而生成器函数yield命令后仅能跟随Promise对象或Thunk函数。

(4)async函数的返回值只能是Promise对象,而生成器函数的返回值是Iterator对象。

至此,关于JavaScript异步编程中async函数的详解介绍完毕。如需了解更多JS async相关内容,请搜索本站往期文章或继续浏览后续文章,感谢大家的支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章