Async/await详解

一、概念与背景

他是在ES8被提出的一种异步方式,它其实是promise的一种语法糖

二、 Async关键字

async 关键字用于快速声明异步函数 ,可以用在函数声明、函数表达式、箭头函数和方法上

async function foo() {} 
let bar = async function() {}; 
let baz = async () => {}; 
class Qux { async qux() {} } 

2.1、特性

  • async声明的异步函数,如果没有返回值,则它就是普通函数,没有什么特别
async function foo() {
	console.log(1); 
} 
foo(); 
console.log(2); 
// 1 
// 2
  • async声明的异步函数,如果有返回值,则返回值会被Promise.resolve自动包装成promise返回
  • async声明的异步函数,可以用await来调用其他异步函数

三、await

该关键字可以暂停异步函数代码的执行,等待解决

let p = new Promise((resolve, reject) => setTimeout(resolve, 1000, 3));
 p.then((x) => console.log(x)); // 3
 
// 使用 async/await 可以写成这样:
async function foo() {
let p = new Promise((resolve, reject) => setTimeout(resolve, 1000, 3));
	console.log(await p);
}
foo(); // 3

3.1、特性

  • await 关键字期待(但实际上并不要求)一个实现 thenable 接口的对象,但常规的值也可以。如果是实现 thenable 接口的对象,则这个对象可以由 await 来“解包”。如果不是,则这个值就被当作 已经解决的期约
// 等待一个原始值
async function foo() {
	console.log(await 'foo'); }
foo(); 
// foo

 // 等待一个没有实现 thenable 接口的对象
 async function bar() {
	console.log(await ['bar']); 
}
bar();
 // ['bar']
 
// 等待一个实现了 thenable 接口的非期约对象 
async function baz() {
  const thenable = {
    then(callback) { 
    	callback('baz');
     }
};
console.log(await thenable); }
baz(); 
// baz

// 等待一个期约 
async function qux() {
	console.log(await Promise.resolve('qux')); 
}
qux();
// qux
  • 如果await等待的结果是抛出了一个错误,或者是一个失败的promise,则不会执行异步函数中后面的代码了,该异步函数就会返回一个失败的promise
async function foo2() { 
	console.log(1);
	const aaa = await (() => { throw 3; })();
	console.log(2)
}
foo2().then(console.log).catch(console.log)
// 1 3


async function foo2() { 
	console.log(1);
	const aaa = await 	Promise.reject(3)
	console.log(2)
}
foo2().then(console.log).catch(console.log)
// 1 3

3.2、await使用场景

await关键字只能在这两个场景下使用:
1、async定义的异步函数中;
2、模块的最外层作用域中(a、html中的script标签声明type='module’类型,b、es6中的模块js文件中)

四、分析执行顺序demo

async function foo() {
    console.log(await Promise.resolve('foo'));
}
async function bar() { 
    console.log(await 'bar');
}
async function baz() { 
    console.log('baz');
}
const p = new Promise(resolve=>{console.log(2222),resolve('promise')})
p.then(v=>console.log(v))
foo();
bar();
baz();

// 输出结果:
2222
baz
promise
foo
bar

代码解析:

  • 首先执行promise的构造函数内部,打印2222,然后执行resolve(‘promise’),将状态改为成功
  • 然后遇到.then()将then中的回调函数放到微任务队列中
  • 执行调用foo函数,遇到console.log(await Promise.resolve(‘foo’)) ;可以将这语句改为同等意思的promise写法new Promise(resolve=>resolve(‘foo’).then(val=>console.log(val))),所以还是一样先执行resolve(‘foo’)状态成功后,将then中的回调放到微任务中
  • 继续调用bar函数,await后面如果是普通值,也会默认被Promise.resolve()方法包装,所以和上面的同理,将console.log(‘bar’)放到微任务队列中
  • 继续执行调用baz函数,由于baz函数内部没有返回值,所以和普通同步函数一样,所以直接打印baz
  • 此时第一次宏任务执行完了,就会执行所有的微任务,于是就有依次打印promise、foo、bar
async function t1() { 
  console.log(1111);
  console.log(await t2());
  console.log(2222);
}
async function t2() { 
  console.log(3333);
 return await 4444
}
t1()
console.log(5555);
setTimeout(() => {
  console.log(6666);
}, 0)
new Promise((resolve, reject) => { 
  console.log(7777);
  resolve()
}).then(() => { 
	console.log(8888); 
})
console.log(9999);

代码解析:

  • 首先调用t1函数,打印1111,然后遇到console.log(await t2()),会先去执行t2,可以改写下t2函数

    function t2(){
    	return new Promise(resolve1=>{
    		console.log(3333);
    		resolve1(new Promise(resolve2=>{
    			resolve2(4444)
    		}))
    	})
    }
    
  • 所以就是先打印3333,执行第一个resolve,状态成功后将resolve1括号中的内容都看成第一个promise的then回调,将这个回调放到微任务队列中

  • 然后打印5555

  • 遇到setTimeout,将其回调放到宏任务队列中

  • 然后执行下面promise构造函数内部代码,打印7777,然后成功后,将then中的console.log(8888); 回调放到微任务队列中

  • 继续执行,打印9999

  • 至此,第一次宏任务执行完了,接下来去执行所有微任务

  • 此时会去执行t2函数中的.then(new Promise(resolve2=>{resolve2(4444)})),执行内部构造函数,将第二个promise状态置为成功后,t2函数执行完成,await t2()等到的结果相当于是一个值为4444 的promise,所以await前面的console.log就又相当于是该promise.then()中的回调,所以再次将console语句放入微任务队列中

  • 此时后面的console.log(2222);语句也不会执行,因为前面的await语句还没执行完,还有个回调没执行完

  • 所以就会继续执行微任务队列中的console.log(8888);

  • 然后发现微任务队列中还存在刚刚放入的console.log(4444)

  • 继续往下执行 console.log(2222);

  • 至此,第一次宏、微任务都执行完了,就会去宏任务队列中找下一个宏任务,发现有,则会执行打印6666

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ronychen’s blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值