async、await是js解决异步编程的一种方案
async function用来声明一个异步函数,这个异步函数返回一个Promise
await后面接一个会返回Promise的函数并执行这个函数,await会暂停async代码的执行,直到Promise执行完
使用async、await会让异步函数的语法和结构更像同步函数
async语法
async function f() {
return 1;
}
等价于
function foo() {
return Promise.resolve(1)
}
async会返回一个promise
返回的Promise对象会运行执行(resolve)异步函数的返回结果,或者运行拒绝(reject)
如果function中返回的是一个值,async直接会用Promise.resolve()包裹一下返回
async function f() {
return 1;
}
f().then((res) => {
console.log(res) // 1
})
async函数内部return语句返回的值,会成为then方法回调函数的参数
async function f() {
return 'hello world';
}
f().then(v => console.log(v)) // "hello world"
await操作符
用于等待一个Promise对象,它只能在异步函数async中使用,await会暂停async的执行,等待Promise处理完成
await的返回值返回Promise对象的处理结果,如果等待的不是Promise对象,则返回该值本身
若Promise异步操作成功,其回调的resolve函数参数作为await表达式的值,继续执行async
若Promise异步操作失败,await会把Promise的异常原因抛出
async在没有遇到await之前async函数是同步运行,遇见await会异步执行
在await表达式之后的代码可以被认为是存在在链式调用的then回调方法中
async function foo(){
await 1
}
等价于
function foo() {
return Promise.resolve(1).then(() => undefined)
}
错误处理(await只等待一个结果,异步发生错误时,如何捕获错误)
使用用try-catch来做错误捕捉
async function myFunction() {
try {
await Promise.reject('1');
} catch (err) {
console.log(err);
}
}
myFunction(); // 1
用promise的catch来做错误捕捉
async function myFunction() {
await Promise.reject('1').catch((err) => {
console.log(err);
});
}
myFunction(); // 1
async函数内部抛出错误,会导致返回的Promise对象变为reject状态,抛出的错误对象会被catch方法回调函数接收到
async function f() {
throw new Error('出错了');
}
f().then(
v => console.log(v),
e => console.log(e)
)
async 返回的Promise对象的状态变化
必须等内部所有await命令后面的Promise对象执行完,才会发生状态改变,或者遇到return语句或者抛出错误
只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数
async function getTitle(url) {
let response = await fetch(url);
let html = await response.text();
return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
只有函数getTitle内部的三个操作全部完成,才会执行then方法里面的console.log
await后面的Promise对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到
async function f() {
await Promise.reject('出错了');
}
f().then(v => console.log(v)).catch(e => console.log(e))
任何一个await后面的Promise对象变为reject状态,那么整个async函数都会中断执行
async function f() {
await Promise.reject('出错了');
await Promise.resolve('hello world'); // 不会执行
}
如果await后面的异步操作出错,那么等同于async函数返回的 Promise 对象被reject
async function f() {
await new Promise(function (resolve, reject) {
throw new Error('出错了');
});
}
f().then(v => console.log(v)).catch(e => console.log(e))
f执行后,await后面的Promise会抛出一个错误对象,导致catch的回调函数被调用,它的参数就是抛出的错误对象
防止出错,使用try catch
async function f() {
try {
await new Promise(function (resolve, reject) {
throw new Error('出错了');
});
} catch(e) {
}
return await('hello world');
}
如果有多个await命令,可以统一放在try catch中
async function main() {
try {
const val1 = await firstStep();
const val2 = await secondStep(val1);
const val3 = await thirdStep(val1, val2);
console.log('Final: ', val3);
}
catch (err) {
console.error(err);
}
}
使用 async和await 的注意事项
1.await命令后面的Promise对象,运行结果可能是rejected,最好把await命令放在try catch中
2.多个await命令后面的异步操作,如果不存在继发关系(互相不依赖),最好让它们同时触发
let foo = await getFoo();
let bar = await getBar();
等价于
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
3.await命令只能用在async函数之中,如果用在普通函数,就会报错
async 和 Promise 的区别
通过示例:在DOM元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个
如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值
使用 Promise
function chainAnimationsPromise(elem, animations) {
let ret = null; // 变量ret用来保存上一个动画的返回值
let p = Promise.resolve(); // 新建一个空的Promise
for(let anim of animations) { // 使用then方法,添加所有动画
p = p.then(function(val) {
ret = val;
return anim(elem);
});
}
return p.catch(function(e) { // 返回一个部署了错误捕捉机制的Promise
}).then(function() { // 忽略错误,继续执行
return ret;
});
}
Promise 的写法全都是Promise的API(then、catch等),操作本身的语义反而不容易看出来
使用 async
async function chainAnimationsAsync(elem, animations) {
let ret = null;
try {
for(let anim of animations) {
ret = await anim(elem);
}
} catch(e) { // 忽略错误,继续执行
}a
return ret;
}
async 函数的实现最简洁,最符合语义,几乎没有语义不相关的代码
实例:按顺序完成异步操作(依次远程读取一组 URL,然后按照读取的顺序输出结果)
Promise 的写法如下
function logInOrder(urls) {
const textPromises = urls.map(url => { // 远程读取所有URL
return fetch(url).then(response => response.text());
});
textPromises.reduce((chain, textPromise) => { // 按次序输出
return chain.then(() => textPromise)
.then(text => console.log(text));
}, Promise.resolve());
}
使用fetch方法,同时远程读取一组URL,每个fetch操作都返回一个Promise对象,放入textPromises数组
reduce方法依次处理每个Promise对象,然后使用then,将所有Promise对象连起来,因此就可以依次输出结果
async 函数实现
async function logInOrder(urls) {
const textPromises = urls.map(async url => { // 并发读取远程URL
const response = await fetch(url);
return response.text();
});
for (const textPromise of textPromises) { // 按次序输出
console.log(await textPromise);
}
}
map方法的参数是async函数,它是并发执行的,因为只有async函数内部是继发执行,外部不受影响
后面的for of循环内部使用了await,因此实现了按顺序输出
总结
async/await 是基于Promise概念的解决异步操作方法,从语义上讲可读性更强,更加容易使用