先上代码

公共代码

function getData(data, time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(data);
}, time)
})
}
let results = [];
let startTime = new Date();
laucher();

代码段一

   async function laucher() {
let dataA = await getData('a', 2000);
results.push(`${dataA}在${new Date() - startTime}毫秒放入`);
let dataB = await getData('b', 3000);
results.push(`${dataB}在${new Date() - startTime}毫秒放入`);
let dataC = await getData('c', 1000);
results.push(`${dataC}在${new Date() - startTime}毫秒放入`);
console.log(results, `输出时间${new Date() - startTime}毫秒`);
}

输出

["a在2002毫秒放入", "b在5004毫秒放入", "c在6006毫秒放入"] "输出时间6006毫秒"

代码段二

 async function laucher() {
let dataAPromise = getData('a', 2000);
let dataBPromise = getData('b', 3000);
let dataCPromise = getData('c', 1000);
let promises = [dataAPromise, dataBPromise, dataCPromise];
results = await Promise.all(promises);
console.log(results, `输出时间${new Date() - startTime}毫秒`);
}

输出

["a", "b", "c"] "输出时间3006毫秒"

代码段三

           async function laucher() {
let dataAPromise = getData('a', 2000);
let dataBPromise = getData('b', 3000);
let dataCPromise = getData('c', 1000);
dataA = await dataAPromise;
results.push(`${dataA}在${new Date() - startTime}毫秒放入`);
dataB = await dataBPromise;
results.push(`${dataB}在${new Date() - startTime}毫秒放入`);
dataC = await dataCPromise;
results.push(`${dataC}在${new Date() - startTime}毫秒放入`);
console.log(results, `输出时间${new Date() - startTime}毫秒`);
}

输出

["a在2003毫秒放入", "b在3001毫秒放入", "c在3001毫秒放入"] "输出时间3002毫秒"

代码段四

     async function laucher() {
let dataAPromise = getData('a', 2000);
let dataBPromise = getData('b', 3000);
let dataCPromise = getData('c', 1000);
(async () => {
dataA = await dataAPromise;
results.push(`${dataA}在${new Date() - startTime}毫秒放入`);
})();
(async () => {
dataB = await dataBPromise;
results.push(`${dataB}在${new Date() - startTime}毫秒放入`);
console.log(results, `输出时间${new Date() - startTime}毫秒`);//results放在最后返回的请求中
})();
(async () => {
dataC = await dataCPromise;
results.push(`${dataC}在${new Date() - startTime}毫秒放入`);
})();
}

输出

["c在1002毫秒放入", "a在2002毫秒放入", "b在3003毫秒放入"] "输出时间3003毫秒"

总结

使用setTimeout模拟了3条异步请求,分别2000,3000,1000毫秒后返回'a', 'b', 'c',
第一种方法很好理解,就是一步一步执行,这种方法适合请求参数依赖上一个请求返回值的情况,在这里是不存在这种关系的,也就是这种方法在这里效率是比较低的。

第二种方法一开始就发起了3个请求,并等待3请求都到达后获取数据。

第三种方法也一开始就发起了3个请求,并在请求到达后依次执行,因为a请求到达时间是2秒,a请求到达后,把a的结果推入results,再往下执行,b请求是3秒,b请求迟a请求一秒到达,也就是再过一秒后把b的结果推入results,,c的请求是1秒,这个时候c早已到达,在这轮循环末尾可以立即把c推入。a请求返回的数据2秒后就能操作了,这种方法比第二种方法可以更快处理数据。如果请求时间是依次递减的,那么和方法二效果是一样,在有多个请求时这种情况一般不存在。

第四种方法和第三种方法的区别是最先到达的请求最快放入结果集,也就是我不用排队等待处理,哪个数据先返回我就先处理哪个数据,假如c请求返回的数据需要花比较长的时间处理,我在一秒后就能开始处理了,但是第三种方法我得3秒后才能开始处理。可以看到我把results的输出放在了b请求到达的函数中,因为results在最后一个请求到达后才能完整输出,与方法三的区别是获取结果的操作也是异步的,这个很关键,也是和方法三最大的区别,通过在外层包装一个自执行函数,可以防止await的操作权跳出laucher外部,从而并发发起3个获取结果的操作。可能大家会有疑问假如我不清楚哪个请求最后到达,那怎么获取最后的results值,这种情况可以在外面包一个Promise.all,可以仔细看一下下面两个函数的区别

  async function laucher() {
let dataAPromise = getData('a', 2000);
let dataBPromise = getData('b', 3000);
let dataCPromise = getData('c', 1000);
let promises = [dataAPromise, dataBPromise, dataCPromise];
results = await Promise.all(promises);
console.log(results, `输出时间${new Date() - startTime}毫秒`);
}

输出

["a", "b", "c"] "输出时间3003毫秒"
    async function laucher() {
let dataAPromise = getData('a', 2000);
let dataBPromise = getData('b', 3000);
let dataCPromise = getData('c', 1000);
let promises = [dataAPromise, dataBPromise, dataCPromise];
results = await Promise.all(promises.map(async function (promise) {
let data = await promise;
console.log(`${data}在${new Date() - startTime}毫秒输出`);
//这里可以提前处理数据
return data
}));
console.log(results);
}

输出

c在1002毫秒输出
a在2003毫秒输出
b在3003毫秒输出
["a", "b", "c"] "输出时间3004毫秒"

如果请求之间不存在继发关系,并且请求到达后要执行一些运算,那么按效率来说
方法4 > 方法3 > 方法2 > 方法1
每种方法都对应一种加载的策略,以一个使用场景来说明一下,向后台加载页面组件(假设组件个数是3)
执行流程:

方法一: 发起组件1的请求 -> 组件1数据到达后渲染组件1 -> 发起组件2的请求 -> 组件2数据到达后渲染组件2 -> 发起组件3的请求 -> 组件3数据到达后渲染组件3

方法二: 同时发起组件1,2,3的请求 -> 组件1,2,3的数据都到达后渲染组件1,2,3

方法三: 同时发起组件1,2,3的请求 -> 组件1数据到达后渲染组件1 -> 组件2数据到达后渲染组件2 -> 组件3数据到达后渲染组件3

方法四: 同时发起组件1,2,3的请求 -> 最快到达的组件数据到达后渲染最快到达的组件 -> 第二快到达的组件数据到达后渲染第二快到达的组件 -> 最慢到达的组件数据到达后渲染最慢到达的组件

针对以上场景可以看出方法四可以让我们的页面最快渲染出内容

最后

可以看出虽然引入async/await,可以让你的代码很精简,但是async/await本身的执行流程却是很复杂的,下面的代码你可以猜一下输出结果(补充一点:有的文章会指出forEach函数不能放入异步操作,这种结论是错误的,如果是继发关系确实不适宜用forEach,但不能表示不能放入异步操作,反而这种操作能提高获取数据的效率)

 [1, 2].forEach(async function (value) {
console.log(value);
await console.log('a');
console.log('c');
await console.log('d');
console.log('e');
})
console.log(3);

async/await 真不是你想象中那么简单的更多相关文章

  1. show()封装没有想象中那么简单

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. 食之无味?App Startup 可能比你想象中要简单

    请点赞关注,你的支持对我意义重大. Hi,我是小彭.本文已收录到 GitHub · AndroidFamily 中.这里有 Android 进阶成长知识体系,有志同道合的朋友,关注公众号 [彭旭锐] ...

  3. 刚刚完成了在vs2013中通过 ef连接mysql数据库的工作。感觉没有想象中的简单。试了n次终于成功。故记录成功的方法,希望可以帮到大家

    分两种情况,如果你是用entity framework 5.0的时候 mysql-connector-net的版本不是很重要. MySQL For VisualStudio的版本也不重要 (这个不装就 ...

  4. 【TypeScript】如何在TypeScript中使用async/await,让你的代码更像C#。

    [TypeScript]如何在TypeScript中使用async/await,让你的代码更像C#. async/await 提到这个东西,大家应该都很熟悉.最出名的可能就是C#中的,但也有其它语言也 ...

  5. [译]async/await中阻塞死锁

    这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的两篇博文中翻译过来. 原文1:Don'tBlock o ...

  6. C# async/await 使用总结

    今天搞这两个关键字搞得有点晕,主要还是没有彻底理解其中的原理. 混淆了一个调用异步方法的概念: 在调用异步方法时,虽然方法返回一个 Task,但是其中的代码已经开始执行.该方法在调用时,即刻执行了一部 ...

  7. C# 异步操作 async await

    在编程的过程中,我们会遇到很多需要异步操作的场景.比如要下载一个文件,如果使用同步的方式进行下载,那么UI操作就会被卡住,这时最好能够使用异步的方式进行下载.在C#中,很早就开始支持异步的操作了,只不 ...

  8. Async/Await替代Promise的6个理由

    译者按: Node.js的异步编程方式有效提高了应用性能:然而回调地狱却让人望而生畏,Promise让我们告别回调函数,写出更优雅的异步代码:在实践过程中,却发现Promise并不完美:技术进步是无止 ...

  9. 浅谈Async/Await

    概要 在很长一段时间里面,FE们不得不依靠回调来处理异步代码.使用回调的结果是,代码变得很纠结,不便于理解与维护,值得庆幸的是Promise带来了.then(),让代码变得井然有序,便于管理.于是我们 ...

随机推荐

  1. Windows7/win10系统安装JDK的环境变量设置javac不是内部命令或外部命令

    ---恢复内容开始--- Windows7/win10系统安装JDK的环境变量设置 Windows7 X64安装“jdk-6u26-windows-x64.exe”后,按照网上的环境变量设置方法设置了 ...

  2. vue项目-axios封装、easy-mock使用

    vue全家桶概括下来就是 项目构建工具(vue-cli) 路由(vue-router) 状态管理(vuex) http请求工具 vue有自己的http请求工具插件vue-resource,但是vue2 ...

  3. mysql数据库备份与恢复命令

    mysqldump -h主机名  -P端口 -u用户名 -p密码 [--databases] 数据库名(可以是多个,用空格分割) > 文件名.sql 备份MySQL数据库的命令(备份脚本中不包含 ...

  4. 判断页面是在移动端还是PC端打开的

    $(function () { var curWwwPath = window.document.location.href; var pathName = window.document.locat ...

  5. MongoDB系列(三):增删改查(CURD)

    上篇讲了MongoDB的基础知识,大家应该对MongoDB有所了解了,当然真正用的还是curd操作,本篇为大家讲解MongoDB的curd操作. 1.数据库操作 #.增 use config #如果数 ...

  6. 5.(基础)tornado异步

    终于到了传说中的异步了,感觉异步这个名字听起来就很酷酷的,以前还不是多擅长Python时,就跑去看twisted的源码,结果给我幼小的心灵留下了创伤.反正包括我在内,都知道异步编程很强大,但是却很少在 ...

  7. Zabbix Agent配置文件详解

    # This is a config file for the Zabbix agent daemon (Unix)# To get more information about Zabbix, vi ...

  8. ZenCart通过Contact Us接收垃圾邮件的过滤方案

    最近收到一些通过Contact Us进行垃圾外链群发的邮件,虽然可以通过在Contact Us增加验证码来解决,但不利于客户体验.所以我们可以通过简单的关键词过滤来实现,一般垃圾外链都含有“[url= ...

  9. 第二章 Vue快速入门-- 25 过滤器-定义格式化时间的全局过滤器

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  10. 过滤器、拦截器、AOP的区别

    过滤器 过滤器可以拦截到方法的请求和响应(ServletRequest request, SetvletResponse response),并对请求响应做出响应的过滤操作,比如设置字符编码.鉴权操作 ...