zoukankan      html  css  js  c++  java
  • 异步编程下篇

    16-4 asyncawait (次重点)

    上一节咱们学习了如何利用生成器实现异步操作。在生成器中,利用yield将异步操作挂起,外部 通过执行器让生成器的代码继续执行。这样,在生成器中,可以将异步的操作做成同步的效果, 实现了异步代码的简化。不过,这种方式需要编写外部的执行器,而执行器的代码写起来一点也 不简单。当然也可以使用一些插件,比如co模块来简化执行器的编写。

    在ES7中,加入了 async函数来处理异步。它实际上只是生成器的一种语法糖而已,简化了外部 执行器的代码,同时利用await替代yield, async替代生成器的*号。下面还是来看个例子:

    async function delay(){

    await new Promise((resolve) => {setTimeout(()=>{resolve()},2000)}); console.log("go on);

    } delay();

    这个例子我们之前用生成器也写过,其中把生成器的(*)号被换成了async。async关键字必须写在 function的前面。如果是箭头函数,则写在参数的前面:

    const delay = async () => {}

    在函数中,第一句用了 await。它替代了之前的yield。后面同样需要跟上一个Promise对象。接下 来的打印语句会在上面的异步操作完成后执行。外部调用时就和正常的函数调用一样,但它的实 现原理和生成器是类似的。因为有了 async关键字,所以它的外部一定会有相应的执行器来执行 它,并在异步操作完成后执行回调函数。只不过这一切都被隐藏起来了,由JS引擎帮助我们完 成。我们需要做的就是加上关键字,在函数中使用await来执行异步操作。这样,可以大大的简 化异步操作。同时,能够像同步方法一样去处理它们。

    接下来我们再来看看更细节的一些问题。await后面必须是一个Promise对象,这个很好理解。因 为该Promise对象会返回给外部的执行器,并在异步动作完成后执行resolve,这样外部就可以通 过回调函数处理它,并将结果传递给生成器。

    那如果await后面跟的不是Promise对象又会发生什么呢?

    const delay = async () => {

    let data = await "hello";

    console.log(data);

    }

    这样的代码是允许的,不过await会自动将hello字符串包装一个Promise对象。就像这样:

    let data = await new Promise((resolve,reject) => resolve("hello"));

    创建了 Promise对象后,立即执行resolve,并将字符串hello传递给外部的执行器。外部执行器的 回调函数再将这个hello传递回来,并赋值给data变量。所以,执行该代码后,马上就会输出字符 hello。虽然代码能够这样写,但是await在这里的意义并不大,所以await还是应该用来处理异 步方法,同时该异步方法应该使用Promise对象。

    async函数里面除了有await关键字外,感觉和其他函数没什么区别,那它能有返回值吗?答案是 肯定的,

    const delay = async () => {

    await new Promise((resolve) => {setTimeout(()=>{resolve()},2000)}); return "finish";

    }

    let result = delay();

    console.log(result);

    在delay函数中先执行等待2秒的异步操作,然后返回字符串finish。外部调用时我用一个变量接收 它的返回值。最后输出的结果是:

    //没有任何等待立即输出

    Promise { <pending> }

    // 2秒后程序结束

    我们可以看到,没有任何等待立即输出了一个Promise对象。而整个程序是在2秒钟后才结束的。 由此看出,获取async函数的返回结果实际上是return出来的一个Promise对象。假如return后面 跟着的本来就是一个Promise对象,那么它会直接返回。但如果不是,则会像await—样包裹一个 Promise对象返回。所以,想要得到返回的具体内容应该这样:

    const delay = async () => {

    await new Promise((resolve) => {setTimeout(()=>{resolve()},2000)}); return "finish";

    }

    let result = delay();

    console.log(result);

    result.then(function(data){

    console.log("data:",data);

    });

    执行的结果:

    //没有任何等待立即输出

    Promise { <pending> }

    //等待2秒后输出

    data: finish

    那如果函数没有任何返回值,得到的又是什么呢?我将上面代码中取掉return,再次运行:

    //没有任何等待立即输出

    Promise { <pending> }

    //等待2秒后输出

    data: undefined

    可以看到,仍然可以得到Promise对象,但由于函数没有返回值,所以就不会有任何数据传递出 来,那么打印的结果就是undefined o

    最后我们还是来梳理一下async的执行顺序。大致的顺序为:先执行同步代码,然后通过执行器 来执行async里面的每一句代码,如果有返回值,在外部要通过then()方法的回调函数来接

    收,最后才被执行,示例如下:

    const delay = async () => {

    console.log('first');

    let data = await new Promise((resolve,reject) => resolve("hello"));

    console.log('aa');

    console.log(data);

    let data2 = await new Promise((resolve) => {setTimeout(()=>{resolve('Yes ')},2000)});

    console.log(data2);

    return 'World';

    }

    let result = delay();

    console.log(result);

    result.then(function(data){

    console.log("data:",data);

    });

    console.log(11);

    console.log(22);

    // first

    // Promise { <pending> }

    // 11

    // 22

    // aa

    // hello

    // Yes

    // data: World

    效果:首先执行async函数,打印出first,然后是暂停里面的代码,返回一个promise,来到外 部。在外部执行完所有同步的代码,输出Promise { vpending> } , 11和22。接下来回到

    async函数,输出aa和hello,然后等两秒钟后,输出Yes,最后回到外部,执行then() 法,打印出data: World。

    async的基本原理我们清楚了,下面我们把之前的AJAX例子用async重写下:

    Mock.mock(/.json/,{

    'stuents|5-10' : [{

    'id|+1' : 1,

    'name' : '@cname',

    'gende r' : /[男女]/, //在正则表达式匹配的范围内随机 'age|15-30' : 1, //年龄在 15-30之间生成, 1只是用来确定数据类型 'phone' : /1d{10}/,

    'addr' : '@county(true)', //随机生成中国的一个省、市、县数据

    'date' : "@date('yyyy-MM-dd')"

    }]

    });

    async function getUsers(){ let data = await new Promise((resolve,reject) => {

    $.ajax({ type:"get", url:"/users.json", success:function(data){ resolve(data)

    }

    });

    }); console.log("data",data);

    }

    getUsers();

    这是用JQuery的AJAX方法实现。

    async function getUsers(){

    let response = await fetch("/users");

    let data = await response.json(); console.log("data",data);

    } getUsers();

    这是f etch方法的实现。

    从这两个例子可以看出,async和生成器两种方式都很类似,但async可以不借助任何的第三方模 块,也更易于理解,async表示该函数要做异步处理。await表示后面的代码是一个异步操作,等 待该异步操作完成后再执行后面的动作。如果异步操作有返回的数据,则在左边用一个变量来接 收它。

    我们知道,await可以让异步操作变为同步的效果。但是,有的时候为了提高效率,我们需要让 多个异步操作同时进行怎么办呢?方法就是执行异步方法时不加await,这样它们就可以同时进 彳丁,然后在获取结果时用await。比如:

    function time(ms){

    return new Promise((resolve,reject) => { setTimeout(()=>{resolve()},ms);

    });

    }

    const delay = async () => {

    let t1 = time(2000);

    let t2 = time(2000);

    await t1;

    console.log("t1 finish"); await t2;

    console.log("t2 finish");

    }

    delay();

    我先把时间函数的异步操作封装成了函数,并返回Promise对象。在dela y函数中调用了两次time 方法,但没有用await。也就是说这两个时间函数的执行是"同时"(其实还是有先后顺序)进行 的。然后将它们的Promise对象分别用t1和t2表示。先用await t1。表示等待t1的异步处理完成, 然后输出t1 finish。接着再用await t2,等待t2的异步处理完成,最后输出t2 finish。由于这两个 时间函数是同时执行,而且它们的等待时间也是一样的。所以,当2秒过后,它们都会执行相应 的回调函数。运行的结果就是:等待2秒后,先输出t1 finish,紧接着立即输出t2 finish。

    const delay = async () => {

    await time(2000);

    console.log("t1 finish");

    await time(2000);;

    console.log("t2 finish");

    }

    如果是这样写,那么执行的结果会是等待2秒后输出t1 finish。再等待2秒后输出t2 finish。async 确实是一个既好用、又简单的异步处理方法。但是它的问题就是不兼容老的浏览器,只有支持了 ES7的浏览器才能使用它。

    最后,还需要注意一个问题:await关键字必须写在async定义的函数中。

  • 相关阅读:
    atan与atan2的区别
    UVALive 6324 Archery (求射箭覆盖的期望)
    哈希UVALive 6326 Contest Hall Preparation
    HDU 2489 Minimal Ratio Tree (DFS枚举+最小生成树Prim)
    UVA:11183:Teen Girl Squad (有向图的最小生成树)
    POJ3164:Command Network(有向图的最小生成树)
    UVA10462:Is There A Second Way Left? (判断次小生成树)
    UVA10600:ACM Contest and Blackout(次小生成树)
    HDU4081:Qin Shi Huang's National Road System (任意两点间的最小瓶颈路)
    HDU1233:还是畅通工程(最小生成树)
  • 原文地址:https://www.cnblogs.com/jrzqdlgdx/p/11350743.html
Copyright © 2011-2022 走看看