async 和 await 是 ES2016 新增两个关键字,它们借鉴了 ES2015 中生成器在实际开发中的应用,目的是简化 Promise api 的使用,并非是替代 Promise。
lodash的forEach和[].forEach不支持await,如果非要一边遍历一边执行await,可使用for-of
async
目的是简化在函数的返回值中对Promise的创建
async 用于修饰函数(无论是函数字面量还是函数表达式),放置在函数最开始的位置,被修饰函数的返回结果一定是 Promise 对**象**。
async function test(){console.log(1);return 2; // 对应的是 resolve(2);}
等效于
function test(){return new Promise((resolve, reject)=>{console.log(1);resolve(2);})}
当其返回结果是Promise
async function test(){console.log(1);return new Promise((resolve, reject) =>{resolve(123)});}test().then(value =>{console.log(value)})

其失败案例
async function love(){setTimeout(() => {if(Math.random() < 0.3){return true// 此处返回无效,setTimeout不支持 Promise 且此处的// 返回的结果是 箭头函数内部的返回值,并不是love的返回值}else{return false}}, 0);}
await
await关键字必须出现在async函数中
await用在某个表达式之前,如果表达式是一个Promise,则得到的是thenable中的状态数据。
如果await的表达式不是Promise,则会将其使用Promise.resolve包装后按照规则运行
await 会等到promise函数运行出结果
如图 await关键字之后画红圈的代码都是写在then()里面的
async function test1(){console.log(1);return 2;}async function test2(){const result = await test1();console.log(result); // 这句代码相当于写在then()内}test2();
等效于
function test1(){return new Promise((resolve, reject)=>{console.log(1);resolve(2);})}function test2(){return new Promise((resolve, reject)=>{test1().then(data => {const result = data;console.log(result);resolve();})})}test2();
rejected状态
async function fun1() {console.log(1)throw "错误"}async function fun2() {try {const value = await fun1()console.log(value)}catch (err){console.log(err)}// 相当于// fun2().then(value =>{// console.log(value)// }, err =>{// console.log(err)// })}fun2()
如果await的表达式不是Promise,则会将其使用Promise.resolve包装后按照规则运行
async function text (){const result = await 1;console.log(result)}text()// 等效于function text(){return new Promise ((resolve, reject) =>{Promise.resolve(1).then(data => {const result = data;console.log(data)resolve()})})}text()
改造计时器函数
function delay(duration) {return new Promise((resolve, reject) => {setTimeout(() => {resolve();}, duration);})}async function biaobai(god) {console.log(`邓哥向${god}发出了表白短信`);await delay(500);return Math.random() < 0.3;}
案例1
// 辅助函数,把传进来的对象拼接成url的字符串function toData(obj) {if (obj === null) {return obj;}let arr = [];for (let i in obj) {let str = i + "=" + obj[i];arr.push(str);}return arr.join("&");}// 封装Ajaxfunction ajax(obj) {return new Promise((resolve, reject) => {//指定提交方式的默认值obj.type = obj.type || "get";//设置是否异步,默认为true(异步)obj.async = obj.async || true;//设置数据的默认值obj.data = obj.data || null;// 根据不同的浏览器创建XHR对象let xhr = null;if (window.XMLHttpRequest) {// 非IE浏览器xhr = new XMLHttpRequest();} else {// IE浏览器xhr = new ActiveXObject("Microsoft.XMLHTTP");}// 区分get和post,发送HTTP请求if (obj.type === "post") {xhr.open(obj.type, obj.url, obj.async);xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");let data = toData(obj.data);xhr.send(data);} else {let url = obj.url + "?" + toData(obj.data);xhr.open(obj.type, url, obj.async);xhr.send();}// 接收返回过来的数据xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {resolve(JSON.parse(xhr.responseText))} else {reject(xhr.status)}}}})}//获取李华所在班级的老师的信息//1. 获取李华的班级id Promise//2. 根据班级id获取李华所在班级的老师id Promise//3. 根据老师的id查询老师信息 Promiseasync function getTeacher() {const stus = await ajax({url: "./data/students.json"})let cid;for (let i = 0; i < stus.length; i++) {if (stus[i].name === "李华") {cid = stus[i].classId;}}const cls = await ajax({url: "./data/classes.json"})let tid;for (let i = 0; i < cls.length; i++) {if (cls[i].id === cid) {tid = cls[i].teacherId;}}const ts = await ajax({url: "./data/teachers.json"})for (let i = 0; i < ts.length; i++) {const element = ts[i];if (element.id === tid) {console.log(element);}}}getTeacher();
与其对比的代码
// 辅助函数,把传进来的对象拼接成url的字符串function toData(obj) {if (obj === null) {return obj;}let arr = [];for (let i in obj) {let str = i + "=" + obj[i];arr.push(str);}return arr.join("&");}// 封装Ajaxfunction ajax(obj) {return new Promise((resolve, reject) => {//指定提交方式的默认值obj.type = obj.type || "get";//设置是否异步,默认为true(异步)obj.async = obj.async || true;//设置数据的默认值obj.data = obj.data || null;// 根据不同的浏览器创建XHR对象let xhr = null;if (window.XMLHttpRequest) {// 非IE浏览器xhr = new XMLHttpRequest();} else {// IE浏览器xhr = new ActiveXObject("Microsoft.XMLHTTP");}// 区分get和post,发送HTTP请求if (obj.type === "post") {xhr.open(obj.type, obj.url, obj.async);xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");let data = toData(obj.data);xhr.send(data);} else {let url = obj.url + "?" + toData(obj.data);xhr.open(obj.type, url, obj.async);xhr.send();}// 接收返回过来的数据xhr.onreadystatechange = function () {if (xhr.readyState === 4) {if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {resolve(JSON.parse(xhr.responseText))} else {reject(xhr.status)}}}})}//获取李华所在班级的老师的信息//1. 获取李华的班级id Promise//2. 根据班级id获取李华所在班级的老师id Promise//3. 根据老师的id查询老师信息 Promiseconst pro = ajax({url: './data/students.json'})//对比代码从这开始pro.then(value => {for (let i = 0; i < value.length; i++) {if (value[i].name === '李华') {return value[i].classId}}}).then(cid => {return ajax({url: "./data/classes.json?id=" + cid,}).then(value => {for (let i = 0; i < value.length; i++) {if (value[i].id === cid) {return value[i].teacherId}}})}).then(tid => {return ajax({url: "./data/teachers.json?id=" + tid,}).then(value =>{for (let i = 0; i < value.length; i++) {if(value[i].id === tid){console.log(value[i])}}})})
案例2
/*邓哥心中有三个女神有一天,邓哥决定向第一个女神表白,如果女神拒绝,则向第二个女神表白,直到所有的女神都拒绝,或有一个女神同意为止用代码模拟上面的场景*/function love(index){return new Promise((resolve, reject) =>{console.log(`邓哥向${index}发出了表白短信`)if(Math.random() < 0.3){resolve(true)}else{resolve(false)}})}(async () => {for(let i = 0 ; i < 10 ; i++){const value = await love(i) // 当前循环等待的Promise没有resolve,下一次循环不运行if(value){console.log(` ${i}回复信息啦`)break;}else{console.log(`${i}没有回复信息`)}}})()
与其做对比的代码
/*邓哥心中有三个女神有一天,邓哥决定向第一个女神表白,如果女神拒绝,则向第二个女神表白,直到所有的女神都拒绝,或有一个女神同意为止用代码模拟上面的场景*/function love(girl) {return new Promise((resolve, reject) => {console.log(`邓哥向女神【${girl}】发出短信`)setTimeout(() => {if (Math.random() < 0.5) {resolve(true)} else {resolve(false)}}, 0);})}let por;let isTrue = false;for (let i = 0; i < 10; i++) {if(i == 0){por = love(i)}por = por.then(value =>{if(value ){console.log(`女神【${i}】回复啦短信`)if(isTrue){console.log(`女神【${i}】Sorry 发错人啦`)}else{isTrue = true;console.log(`女神【${i}】是你就是你`)}}else{console.log(`女神【${i}】没有回复啦短信`)}if(i < 9){return love(i + 1);}})}
