一开始写文章的时候太晚了,代码有一处复制错误,已改正,谢谢博友提醒,保证以后不会再犯。

  上一篇博客我们在现实使用和面试角度讲解了Promise(原文可参考《面向面试题和实际使用谈promise》),但是Promise 的方式虽然解决了 callback hell,但是这种方式充满了 Promise的 then() 方法,如果处理流程复杂的话,整段代码将充满 then,代码流程不能很好的表示执行流程。

为什么是async/await

  在es6中,我们可以使用Generator函数控制流程,如下面这段代码:

function* foo(x) {
yield x + 1;
yield x + 2;
return x + 3;
}

  我们可以根据不断地调用Generator对象的next()方法来控制函数的流程。但是这样仿佛不是那么的语义化。因此,在ES6中封装了Generator函数的语法糖async函数,但是将其定义在了es7中。ES7定义出的async 函数,终于让 JavaScript 对于异步操作有了终极解决方案。Async 函数是 Generator函数的语法糖。使用 关键字 Async 来表示,在函数内部使用 await来表示异步。相较于 Generator,Async函数的改进在于下面几点:Generator 函数的执行必须依靠执行器,而 Async() 函数自带执行器,调用方式跟普通函数的调用一样。Async 和 await相较于 * 和 yield 更加语义化。async 函数返回值是 Promise 对象,比 Generator函数返回的 Iterator 对象方便,可以直接使用 then()方法进行调用。

  那么,我们通过一段小小的代码来说明async/await函数的用法:

    未使用async/await的定时函数:

fn = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 2000)
})
}
const Fn = () =>{
fn().then((res) => {
console.log(res)
})
  console.log(2)
} Fn() 

  我相信能看到这里的各位程序员大佬应该都知道这段代码的输出状况:先打印2,2s之后打印出1。

    使用async/await的定时函数:

fn = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 2000)
})
}
const Fn = async () => {
await fn().then((res) => {
console.log(res)
})
}
Fn()
console.log(2)

  这一段函数的输出状况是:2s后打印1,然后打印2。

  那么,why?

  我们在字面上理解这两个单词async和await:async的意思是异步,async用于定义一个异步函数,该函数返回一个Promise。;await的意思是等待,Promise是一个承诺,await也是一个承诺。Promise的承诺是将返回值输出到then的回掉函数里面,无论是成功还是失败。await的承诺是无论刮风还是下雨,我都会等你完成在做其他的步骤。因此,在上面的运用了async/await的代码中,会等待fn完全运行完成并且异步的回调完成对返回值的处理之后在开始进行下一步操作的。其原理是将异步函数转变为同步操作。

实际运用

  在上周的工作中,我在一段基于node完成的爬虫操作中多次运用async/await来控制程序的执行流程:

//伪代码
let axiosArr = [];
for (let i = 0, len = arr.length; i < len; i++) {
let params = qs.stringify({
'param': arr[i].index,
})
axiosArr.push(axios.post(url, params, {
headers
}))
}
/*
*上面的循环是循环抓取2345条数据,平均每个数据要访问16个接口
*用axios.all同时询问,当返回结束后将返回值处理
*然后将返回值存储到mongodb数据库中
*/
await axios.all(axiosArr).then(
axios.spread(function () {
for (let i = 0, len = arguments.length; i < len; i++) {
let str = `${unescape(arguments[i].data.replace(/\\u/g, '%u'))}`;
str = basics.subStr(basics.deletN(basics.deletS(basics.cutStr(str))));
concentArr[i].concent = str
}
mg.mongodbMain({
name: obj.name,
alias: obj.alias,
type: type,
url: obj.url,
drugsConcent: concentArr
})
}))

  其实操作就这么点,大家看一下代码都会懂。但是问题是,当我不使用async/await时,会产生的情况是会先访问2000+个数据,不断访问其16个接口,但是由于promise的then的回调函数为异步的,会挂起,而不是直接将数据存到数据库中。这貌似和我们预想的不一样啊。因此,我在这里使用了async/await函数,使用同步处理异步操作,将promise同步化,当axios.all访问完成这每一条数据的16个接口后,直接将数据存储到数据库中,然后才会走到循环的下一层,依旧是访问下一条数据的16个接口。

async/await的身后事

  我们说过了async 函数返回值是 Promise 对象。

const delay = timeout => new Promise(resolve=> setTimeout(resolve, timeout));
async function f(){
await delay(1000);
await delay(2000);
await delay(3000);
return 'done';
} f().then(v => console.log(v));// 6s之后打印'done'

  那么其内部一旦抛出异常,则会导致返回的 Promise 对象状态变为 reject 状态。抛出的错误而会被 catch 方法回调函数接收到。

async function e(){
throw new Error('error');
}
e().then(v => console.log(v))
.catch( e => console.log(e));//抛出的错误会被catch捕捉到

  并且,async有一个和promise.all相似的特性,就是内部一点有一个await函数报错,后续的就不再执行了

let fn1 = ()=>{
return new Promise((resolve,reject) => {
setTimeout(()=>{
reject('故意抛出错误');
},500);
});
} let fn2 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1);
},500);
});
} let getList = async ()=>{
let a = await fn1();
let b = await fn2();
return {first: a,second:b};
}
getList().then(result=> {
console.log(result);
}).catch(err=> {
console.log(err);// 由于fn1的报错,async的状态直接变成了rejected
});

  当Promise出现的时候,我们仿佛看到了回调地狱的灭亡。当Async/Await出现时,异步终于不是一件困难的事情。

  我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=29r5xhtmccn4k

async/await,了解一下?的更多相关文章

  1. async & await 的前世今生(Updated)

    async 和 await 出现在C# 5.0之后,给并行编程带来了不少的方便,特别是当在MVC中的Action也变成async之后,有点开始什么都是async的味道了.但是这也给我们编程埋下了一些隐 ...

  2. [.NET] 利用 async & await 的异步编程

    利用 async & await 的异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/5922573.html  目录 异步编程的简介 异 ...

  3. [.NET] 怎样使用 async & await 一步步将同步代码转换为异步编程

    怎样使用 async & await 一步步将同步代码转换为异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6079707.html  ...

  4. [.NET] 利用 async & await 进行异步 IO 操作

    利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html  序 上次,博主 ...

  5. [C#] 走进异步编程的世界 - 开始接触 async/await

    走进异步编程的世界 - 开始接触 async/await 序 这是学习异步编程的入门篇. 涉及 C# 5.0 引入的 async/await,但在控制台输出示例时经常会采用 C# 6.0 的 $&qu ...

  6. ASP.NET 中的 Async/Await 简介

    本文转载自MSDN 作者:Stephen Cleary 原文地址:https://msdn.microsoft.com/en-us/magazine/dn802603.aspx 大多数有关 async ...

  7. C# async/await 使用总结

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

  8. 【转】async & await 的前世今生

    async 和 await 出现在C# 5.0之后,给并行编程带来了不少的方便,特别是当在MVC中的Action也变成async之后,有点开始什么都是async的味道了.但是这也给我们编程埋下了一些隐 ...

  9. async & await 的前世今生

    async 和 await 出现在C# 5.0之后,给并行编程带来了不少的方便,特别是当在MVC中的Action也变成async之后,有点开始什么都是async的味道了.但是这也给我们编程埋下了一些隐 ...

  10. C# Async, Await and using statements

    Async, Await 是基于 .NEt 4.5架构的, 用于处理异步,防止死锁的方法的开始和结束, 提高程序的响应能力.比如: Application area           Support ...

随机推荐

  1. Oracle RAC环境下定位并杀掉最终阻塞的会话

    实验环境:Oracle RAC 11.2.0.4 (2节点) 1.模拟故障:会话被级联阻塞 2.常规方法:梳理找出最终阻塞会话 3.改进方法:立即找出最终阻塞会话 之前其实也写过一篇相关文章: 如何定 ...

  2. CentOS 7 安装Graphite

    Graphite简介 Graphite是一个Python编写的企业级开源监控工具,采用django框架,用来收集服务器所有的即时状态,用户请求信息,Memcached命中率,RabbitMQ消息服务器 ...

  3. datable转xml

    /// <summary> /// datatable转换xml /// </summary> /// <param name="xmlDS"> ...

  4. LeetCode & 118-Pascal's Triangle-Easy

    Array Description: Given numRows, generate the first numRows of Pascal's triangle. For example, give ...

  5. js控制表格实时编辑

    点击添加,在表格的最后一行添加一行表单元素,右侧按钮变为保存和取消.(点击保存,数据用ajax无刷新添加到界面,点击取消,取消此行的添加.)点击编辑,在本行改为表单,带有原来的值,右侧按钮变为确认和取 ...

  6. 开源软件:NoSql数据库 - 图数据库 Neo4j

    转载自原文地址:http://www.cnblogs.com/loveis715/p/5277051.html 最近我在用图形数据库来完成对一个初创项目的支持.在使用过程中觉得这种图形数据库实际上挺有 ...

  7. XML之XPath

    1.在 XPath 中,有七种类型的节点:元素.属性.文本.命名空间.处理指令.注释以及文档节点(或称为根节点). 1.1 XPath 术语 节点(Node) 在 XPath 中,有七种类型的节点:元 ...

  8. Python系列-python文件操作

    原链接:https://blog.csdn.net/m0_37745438/article/details/79573414 python提供了一系列方法来对文件进行读取.写入等操作 一.打开文件的方 ...

  9. SpringBoot实现多环境配置

    1.为什么需要配置多环境配置 在实际的开发中,我们往往需要在不同的环境中使用不同的数据库.缓存配置,如果使用同一套配置文件,在不同环境部署的时候手动去修改配置文件,会使部署变得很繁琐.使用多环境配置文 ...

  10. java配置环境变量,无法也行javac问题

    最近换了公司,搭建开发环境的时候出了点小差错,写篇随笔记录下,下载jdk的时候要选择符合自己电脑的jdk版本,位数. 笔者之前下载的时候没注意下了个32bit的jdk,开发用eclipse的时候打不开 ...