美文网首页
Javascript 开发:用「async / await」语法

Javascript 开发:用「async / await」语法

作者: you的日常 | 来源:发表于2021-11-30 18:16 被阅读0次

    Promise虽然可以将异步程序用看起来像同步程序的方式来撰写,但写法还是与实际的同步程序有不小的差异。也因为Promise有这样的问题,所以后来的 ES2017 引入了「async / await」语法,可以完全地以同步程序的写法使用异步程序。

    「async / await」是Promise和产生器的语法糖

    在前面的章节中,我们有看过以下的程序:

     async function infiniteLoop() {
         while (1) {
        }
     }
    
     infiniteLoop();
     console.log("Hello!");
    

    「async / await」语法的功能是用 ES6 添加的Promise和产生器来实现的。加上 async 关键字的函数,会变成异步函数,其主体会变成产生器函数的主体(不过我们无法直接在异步函数中使用yield关键字),而异步函数的主体中会再创建Promise对象实体来将产生器函数产生出来的迭代器的迭代结果当作「正确值」回传出来(用resolve函数)。异步函数则会将这个Promise对象回传出来。

    如下:

    function infiniteLoop() {
         const generator = function* () {
             while (1) {
             }
         };
    
         return new Promise<void>(function (resolve) {
             const iterator = generator();
             const result = iterator.next();
             resolve(result.value);
       });
     }
    
     infiniteLoop();
    console.log("Hello!");
    

    以上程序,会在第 10 行陷入无穷循环。注意,Hello!文本并不会被输出,因为传入Promise对象建构子的回呼函数是会被同步运行的,换句话说它不会等到 JavaScript 线程进入事件循环后才被调用。

    通过上面的转换说明,我们可以知道原先这个infiniteLoop异步函数的回传值类型为Promise<void>。所以若要明确地写出infiniteLoop异步函数的回传值类型,就会变成以下这样:

    async function infiniteLoop(): Promise<void> {
        while (1) {
        }
    }
    
    infiniteLoop();
    console.log("Hello!");
    

    我们试着让异步函数回传随便的值出来看看,程序如下:

    async function f(): Promise<number> {
        return 123;
     }
    
     f().then((value) => {
             console.log(value);
         })
         .catch((err) => {
            console.error(err);
       });
    
    console.log("Hello!");
    

    以上程序的输出结果为:

    > Hello!
    > 123
    

    传入Promise对象实体的then方法的回呼函数需要通过微任务来调用,所以它在 JavaScript 线程进入事件循环后才会被运行,因此Hello!文本会在123之前输出。

    以上程序,用Promise对象和产生器转换如下:

    function f() {
        const generator = function* () {
            return 123;
        };
    
        return new Promise<number>(function (resolve) {
            const iterator = generator();
    
            const result = iterator.next();
    
            resolve(result.value);
        });
    }
    
    f()
        .then((value) => {
            console.log(value);
        })
        .catch((err) => {
            console.error(err);
        });
    
    console.log("Hello!");
    
    

    您可以能会有这样的疑问:为什么要用到产生器?这是因为await关键字要依靠产生器的yield关键字功能来实作。

    await关键字可以用来「等待」一个Promise对象实体结束运行,并将「正确值」直接回传;将「错误值」直接用throw关键字抛出。例如:

    async function f(): Promise<number> {
        return 123;
    }
    
    try {
        const value = await f();
    
        console.log(value);
    } catch (err) {
        console.error(err);
    }
    
    console.log("Hello!");
    
    

    当然,以上程序是无法编译的,因为我们无法在产生器函数之外使用yield关键字(await关键字需要被转换为yield关键字),所以await关键字必须要用在异步函数之中。

    为了让以上程序能够成功编译,可以将其改写如下:

    async function f(): Promise<number> {
        return 123;
    }
    
    async function main(): Promise<void> {
        try {
            const value = await f();
    
            console.log(value);
        } catch (err) {
            console.error(err);
        }
    
        console.log("Hello!");
    }
    
    main();
    

    以上程序的输出结果为:

    > 123
    > Hello!
    

    以上程序,用Promise对象和产生器转换如下:

    相关文章

      网友评论

          本文标题:Javascript 开发:用「async / await」语法

          本文链接:https://www.haomeiwen.com/subject/gjaiwltx.html