JS async await

本文介绍了async函数,它是AsyncFunction构造函数的实例,函数内可使用await关键字,是Generator函数的语法糖。阐述了async和await的语义,以及async函数的执行顺序、错误处理方式。还对比了继发和并发执行异步任务的情况,并给出了相关示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

async函数是使用async关键字声明的函数。 async函数是AsyncFunction构造函数的实例,函数内允许使用await关键字。async和await关键字让我们可以用一种更简洁的方式写出基于Promise的异步行为,而无需刻意地链式调用promise。

async 基础

async 函数是什么

Async 函数是 Generator 函数的语法糖。async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await

async函数对 Generator 函数的改进

内置执行器

async 函数直接调用就执行了,Generator 函数需要通过next()调用

更好的语义

async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

返回值是 Promise

比操作 Generator 函数返回的Iterator方便。async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

const readFile=(path)=>new Promise((resolve,reject)=>{
  setTimeout((way) => {
	  console.log(way);
      resolve('resolved');
    }, 2000,path);
})
//Generator
function* gen(){
	yield readFile('gen1');
	yield readFile('gen2');
}
//AsyncFunction
async function f(){
	await readFile('async1');
	await readFile('async2');
}

let it=gen();
it.next();
it.next();

f()

在这里插入图片描述

async函数的特性

返回Promise 对象

任何函数没有设置return语句,只要函数执行没有发生错误,默认都是return undefined

async函数只要没有发生异常,return语句返回的值,会作为成功回调函数(return value相当于resolve(value))的参数。

正常执行

📌没有return

async function f(){}//函数不设置默认值救护返回udeffiend 自动包装成promise 
console.log(f())//Promise {<fulfilled>: undefined}

📌有return

async function f(){return 'ok';}
f().then(value=>{console.log(value);},err=>{})//ok
内部报错
async function f(){throw new Error('error')}
f();

在这里插入图片描述

错误捕捉

📌方式一:直接在async函数内部捕捉 返回值做成功回调处理

async function f(){try{
	throw new Error('error')
}catch(e){
	//TODO handle the exception
}}
console.log(f());//Promise {<fulfilled>: undefined}

📌方式二:对返回的Promise对象做错误处理
可以在then()方法第二个参数做失败回调,也可以用catch()捕捉

async function f(){throw new Error('error')}
let p=f();
p.catch((e)=>{
	console.log(e); // Error: error
})

await

  • await 只能放在async函数中。
  • await表达式会暂停整个async函数的执行进程并出让其控制权,只有当其等待的基于promise的异步操作被兑现或被拒绝之后才会恢复进程。
  • 假如await表达式后面跟着的不是Promise值,会通过Promise.resolve(value)将其转换为Promise。
function timeout(ms){
	return new Promise(resolve=>{
		setTimeout(resolve,ms)
	})
} 

async function asyncPrint(){
	console.log(new Date())
	await timeout(2000);
	console.log(new Date())
	await timeout(1000);
	console.log(new Date())
	return 1;
}
let value=asyncPrint();
console.log(value);
setTimeout(()=>{console.log(value);},4000)

在这里插入图片描述
完成后Promise状态发生改变
在这里插入图片描述
执行顺序:
1.asyncPrint()进入异步函数,打印当前时间。
2.await 需要等待promise的异步操作完成,async函数交出控制权,继续回到全局代码块往下执行,打印函数返回值。此时没有执行完毕,处于pending状态。随后将定时器的函数加入事件循环队列。
3.以此类推。。。

为什么第一次和第二次查看的处于pending状态的Promise的值不一样?
第一次查看的时候还在执行!第二次查看的时候执行完成!点击查看具体属性时,控制台的数据刷新挂起了。

异步任务继发与并发

继发与并发

继发:等前面的异步任务执行完成,才开始执行下一个异步任务。(保证异步任务执行顺序 耗时多)
并发:前后的异步任务,谁也不用等谁,可以同时进行。

await执行Promise任务普遍都是继发的。

如何让await请求并发呢

异步请求在子函数中起作用,不会阻塞父函数

比如用map,forEach循环,把await放到循环体里面

请求并发后怎么顺序输出结果呢?

前面通过map返回了一个promise数组

可以用for循环,每个promise来一次await


继发

function getStream(ms) {
	return new Promise(resolve => {
		setTimeout(resolve, ms)
	})
}

function getJson(ms) {
	return new Promise(resolve => {
		setTimeout(resolve, ms)
	})
}

async function f() {
	console.log(new Date());
	await getStream(2000);
	await getJson(2000);
	console.log(new Date());//前后间隔4s
}
f();

并发

// 写法一
async function f() {
	console.log(new Date());
	let [json, stream] = await Promise.all([getJson(2000), getStream(2000)])
	console.log(new Date());//前后间隔2s
}


// 写法二
async function f() {
	console.log(new Date());
	let streamPromise = getStream(2000);
	let jsonPromise = getJson(2000);
	await streamPromise;
	await jsonPromise;
	console.log(new Date());//前后间隔2s
}

方法一:
合并多个Promise对象为一个,await就等待一次组装后的Promise对象,其他的成员Promise对象并发。

方法二:
在异步函数中调用(同步)子函数去做异步请求,这两个(同步)子函数(是并发的)不会阻塞父函数(异步函数),子函数和父函数运行环境是各自独立的。然后获取同步方法返回的Promise对象,await只是保证这个请求已经结束。不设置await就是瞬间执行完异步函数代码块,而此时的streamPromise 和jsonPromise 还处于pending状态。

并发请求顺序输出

页面按顺序显示多张图片

async function logInOrder(urls) {
  // 并发读取远程URL
  const textPromises = urls.map(async url => {
    const response = await fetch(url);
    return response.text();
  });

  // 按次序输出
  for (const textPromise of textPromises) {
    console.log(await textPromise);
  }
}

上面代码中,虽然map()的参数是async函数,但它是并发执行的。

map()中每次遍历形成的{},该{}是一个独立执行环境,每个请求任务要花时间,交出{}的执行权,继续遍历下一个,形成新的{},几个任务同时请求,任务都还在执行,此时这几个任务关系为并发

因为只有async函数内部是继发(根据数组顺序)执行,外部不受影响。后面的for..of循环内部使用了await,因此实现了按顺序输出。

参考文档:
async 函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值