美文网首页基础前端
Promise 结合 async 和 await

Promise 结合 async 和 await

作者: CondorHero | 来源:发表于2019-07-19 15:25 被阅读0次

    上篇文章算是写了 Promise 的基础入门,这次结合 async 和 await 在对异步回调进行深入学习。

    上篇文章: ES6 的 promise 和异步捕获错误 catch

    一、Promise 深入学习

    上篇文章写了 Promise 对异步回调黑洞的解救。但是却又陷入链式黑洞(或火车黑洞)。

    • node fs 依次打开六个文件
    1. 例如我们用 node.js fs 模块依次打开六个 TXT 文件。
    const fs = require("fs");
    
    fs.readFile("./1.txt",function(err,data){
        console.log(data.toString());
        fs.readFile("./2.txt",function(err,data){
            console.log(data.toString());
            fs.readFile("./3.txt",function(err,data){
                console.log(data.toString());
                fs.readFile("./4.txt",function(err,data){
                    console.log(data.toString());
                    fs.readFile("./5.txt",function(err,data){
                        console.log(data.toString());
                        fs.readFile("./6.txt",function(err,data){
                            console.log(data.toString());
                        });
                    });
                });
            });
        });
    });
    
    六首古诗

    代码毕竟是给人看的,但上面的代码,结构松散,难于读取。

    1. 然后我们再用 Promise 改写。
    const fs = require("fs");
    
    function read(url){
        return new Promise(function(resolve,reject){
            fs.readFile(`./${url}.txt`,function(err,data){
                if(err)return;
                resolve(data.toString());
            });
        });
    }
    read(1).then(function(data){
        console.log(data);
        read(2).then(function(data){
            console.log(data);
            read(3).then(function(data){
                console.log(data);
                read(4).then(function(data){
                    console.log(data);
                    read(5).then(function(data){
                        console.log(data);
                        read(6).then(function(data){
                            console.log(data);
                        });
                    });
                });
            });
        });
    });
    

    看了这个写法是不是要骂街了,这也没发挥出 Promise 作为语法糖的作用。所以如果是单纯异步回调改写成 promise 是没啥大区别。打败了回调黑洞进入链式黑洞。但是结合async 和 await 就大不一样了。

    1. 结合async 和 await 再次改写:
    const fs = require("fs");
    
    function read(url){
        return new Promise(function(resolve,reject){
            fs.readFile(`./${url}.txt`,function(err,data){
                if(err)return;
                resolve(data.toString());
            });
        });
    }
    async function main(){
        var data1 = await read(1);
        console.log(data1);
        var data2 = await read(2);
        console.log(data2);
        var data3 = await read(3);
        console.log(data3);
        var data4 = await read(4);
        console.log(data4);
        var data5 = await read(5);
        console.log(data5);
        var data6 = await read(6);
        console.log(data6);
    }
    main();
    

    卧槽,这代码是绝了,结构简单逻辑清晰。看着就很舒服。代码最牛的是实现了 异步调用同步读取。而且省略了 then ,resolve 调用 then 里面的函数,函数的结果直接返回到等于号前面的 data 里面。

    根据上面讲的可以发现 then 的功能被替代掉了,那还有没有 then 这个功能那?答案是有的比如我们依然依次打开三个 TXT 文件,每个文件里面都是一个 JSON 。

    //a.txt
    {"result":"I"}
    =====
    //b.txt
    {"result":"Love"}
    =====
    //c.txt
    {"result":"You"}
    

    然后改写代码读取文件里面的 JSON 里面 result 的属性值。

    const fs = require("fs");
    
    function read(url){
        return new Promise(function(resolve,reject){
            fs.readFile(`./${url}.txt`,function(err,data){
                if(err)return;
                resolve(JSON.parse(data));
            });
        });
    }
     async function main(){
        var data1 = await read("a").then(res=>res.result);
        console.log(data1);
        var data2 = await read("b").then(res=>res.result);
        console.log(data2);
        var data3 = await read("c").then(res=>res.result);
        console.log(data3);
    }
    main();
    
    I Love You

    可见 then 还是可以使用的,要想对返回的结果进行处理,then 是必要的。

    解说上面的 async 和 await 例子:我们做一个主函数,比如叫做 main(),给这个函数加上 async 关键字,表示这个函数里面有异步的语句需要等待。异步语句的前面,加上 await 字样,表示这条语句是异步语句,等待该 Promise 对象的 resolve() 方法执行并且返回值后才能继续执行。

    async 和 await 含义?

    async 用于声明一个异步函数, 该函数需返回一个 Promise 对象. 而 await 通常后接一个 Promise对象, 需等待该 Promise 对象的 resolve() 方法执行并且返回值后才能继续执行. (如果await后接的是其他对象, 便会立即执行) await 只能用于 async 声明的函数上下文中

    如果不加 await 我们答应的结果就是一个Promise 对象。


    不加 await

    如果不加 async ,直接报错。


    不加async
    • animate 异步改写
      我们在来个更牛的 JQuery 的异步函数 animate 改写成同步的。现在书写代码依次让四个盒子从左运动到右边。
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>canvas事件</title>
        <style>
            div {
                width: 60px;
                height: 60px;
                background-color: #0f0;
                margin:10px;
                position: relative;
            }
        </style>
    </head>
    <body>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
    </body>
    </html>
    <script src="./js/jquery-3.4.1.min.js"></script>
    <script>
        $("div").eq(0).animate({"left":"600px"},2000);
        $("div").eq(1).animate({"left":"600px"},2000);
        $("div").eq(2).animate({"left":"600px"},2000);
        $("div").eq(3).animate({"left":"600px"},2000);
    </script>
    

    animate 是异步的所以会一起运动。



    想要实现四个盒子依次运动就得用 callback 回调函数来解决。


    回调结果
        $("div").eq(0).animate({"left":"600px"},2000,function(){
            $("div").eq(1).animate({"left":"600px"},2000,function(){
                $("div").eq(2).animate({"left":"600px"},2000,function(){
                    $("div").eq(3).animate({"left":"600px"},2000);
                });
            });
        });
    

    回调看着乱七八槽使用 Promise 代替

        function move(number){
            return new Promise(function(resolve,reject){
                // 注意这里的 resolve 函数,是作为回调函数调用的
                $("div").eq(number).animate({"left":"600px"},2000,resolve);
            });
        }
        move(0).then(function(){
            move(1).then(function(){
                move(2).then(function(){
                    move(3).then(function(){});
                });
            });
        });
    

    Promise 看着也不是太好,现在用异步语句来实现同步调用。

    function move(number){
            return new Promise(function(resolve,reject){
                $("div").eq(number).animate({"left":"600px"},2000,resolve);
            });
        }
        async function main(){
            await move(0);
            await move(1);
            await move(2);
            await move(3);
        }
        main();
    

    其实还可以在变,由Promise then 的写法变成 Generator 的 next 写法。generator 结合 async 使用。

    function move(number){
            return new Promise(function(resolve,reject){
                $("div").eq(number).animate({"left":"600px"},2000,resolve);
            });
        }
        async function* main(){
            yield move(0);
            yield move(1);
            yield move(2);
            yield move(3);
        }
        var g = main();
        g.next();
        g.next();
        g.next();
        g.next();
    
    二、Generator 的使用

    形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。

    Generator 函数和普通函数其实很像,调用方法和 Promise 的调用方法也很像,下面代码直接调用 main() 函数,会返回一个 Generator 对象,对象在打点调用 next 方法,yield 就像函数里面的 return 把自己后面的值返回,这时变量接收 a 得到的返回状态值是一个对象 {value: "0", done: false} ,很明显 value 后面是yield 后面的返回值,done 是表示函数体里面的语句是否全部执行完毕。现在 var a = g.next(); 只 next 函数了一个,函数现在暂停在第一个遇到的 yield 。继续 next 调用会开启第二个 yield 。

        function* main(){
            yield "0";
            yield "1";
            yield "2";
            yield "3";
        }
        //g 现在是 Generator对象
        var g = main();
        // a现在也是一个对象值为{value: "0", done: false}
        var a = g.next();
        console.log(g,a);
    

    现在让函数体内的语句全部执行完。通过 while 去判断 res 对象里面的属性 done 的值。

    function* main(){
            yield "0";
            yield "1";
            yield "2";
            yield "3";
        }
        //g 现在是 Generator对象
        var g = main();
        // a现在也是一个对象值为{value: "0", done: false}
        var res = g.next();
        // 遍历函数体
        while(!res.done){
            console.log(res.value);
            // 继续调用函数
            res = g.next();
        }
    

    现在给函数体的开始加上 return 语句;这时返回结果value: 5, done: true} return 后面的内容被返回,且函数体全部语句显示执行完毕。

        function* main(){
            return 5;
            yield "0";
            yield "1";
            yield "2";
            yield "3";
        }
        //g 现在是 Generator对象
        var g = main();
        // a现在也是一个对象值为{value: "0", done: false}
        var res = g.next();
        console.log(res);
    

    现在把 return 语句拿到函数体的最下面;这时返回结果:

    0
    1
    2
    3
    

    很明显函数体执行完 return 的内容没有接收到。

        function* main(){
            yield "0";
            yield "1";
            yield "2";
            yield "3";
            return 5 ;
        }
        //g 现在是 Generator对象
        var g = main();
        // a现在也是一个对象值为{value: "0", done: false}
        var res = g.next();
        while(!res.done){
            console.log(res.value);
            res = g.next();
        }
    

    相关文章

      网友评论

        本文标题:Promise 结合 async 和 await

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