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("&");
}
// 封装Ajax
function 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查询老师信息 Promise
async 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("&");
}
// 封装Ajax
function 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查询老师信息 Promise
const 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);
}
})
}