async
和await
本质上是生成器函数yield
的语法糖。
在这之前如果程序中的其他代码要访问Promise
的值,则需要写一个解决处理程序:
let p = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve(3);
}, 1000)
});
p.then((res) => console.log(res)); // 3
这其实是很不方便的,因为其他代码都必须塞到then
处理程序中。为此,ECMAScript
对函数进行了扩展,为其增加了两个新关键字:async
和await
。
async
**async**
关键字用于声明异步函数。这个关键字可以用在函数声明、函数表达式、箭头函数和方法上:
async function foo() {}
let bar = async function() {};
let baz = async () => {};
class Qux {
async qux() {}
}
使用async
关键字可以让函数具有异步特征,但总体上其代码仍然是同步求值的。
async function foo() {
console.log(1);
}
foo(); // 1
console.log(2); // 2
如果异步函数内return
值,这个值会被Promise.resolve()
包装成一个Promise
对象。异步函数始终返回Promise
对象。
async function read() {
return "abc";
}
console.log(read()); // Promise {<fulfilled>: 'abc'}
read().then((res) => {
console.log(res); // abc
});
如果异步函数内发生错误,会直接将Promise
的状态更改为失败:
async function read() {
console.log(a);
return "abc";
}
read().catch((err) => {
console.log(err); // ReferenceError: a is not defined
});
当异步函数内返回失败状态后,下面的代码将不会继续执行:
async function read() {
let res = await Promise.reject("错误");
console.log(123); // 不执行
return res;
}
read().catch((err) => {
console.log(err); // 错误
});
await
因为异步函数主要针对不会马上完成的任务,所以自然需要一种暂停和恢复执行的能力。使用**await**
关键字可以暂停异步函数代码的执行,等待解决。
let p = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve(3)
}, 1000)
});
p.then((res) => console.log(res)); // 3
// 使用 async/await 可以写成这样:
async function foo() {
let p = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve(3)
}, 1000)
});
console.log(await p);
}
foo(); // 3
注意,await
关键字会暂停执行异步函数后面的代码,让出JavaScript
运行时的执行线程。await
关键字同样是尝试“解包”对象的值,然后将这个值传给表达式,再异步恢复异步函数的执行。
// 异步打印 "foo"
async function foo() {
console.log(await Promise.resolve('foo'));
}
foo(); // foo
// 异步打印 "bar"
async function bar() {
return await Promise.resolve('bar');
}
bar().then(console.log); // bar
// 1000 毫秒后异步打印 "baz"
async function baz() {
await new Promise((resolve, reject) => {
setTimeout(()=>{
resolve()
}, 1000)
});
console.log('baz');
}
baz(); // baz(1000 毫秒后)
**JavaScript**
运行时在碰到**await**
关键字时,会记录在哪里暂停执行。等到**await**
右边的值可用了,**JavaScript**
运行时会向消息队列中推送一个任务,这个任务会恢复异步函数的执行。
function request() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2);
}, 3000);
});
}
async function getData() {
let res = await request();
console.log(3);
return res;
}
console.log(1);
getData().then((res) => {
console.log(res);
});
console.log(4);
// 1
// 4
// 3
// 2
await
关键字必须在异步函数中使用,不能在顶级上下文如<script>
标签或模块中使用。不过, 定义并立即调用异步函数是没问题的。
async function foo() {
console.log(await Promise.resolve(3));
}
foo(); // 3
// 立即调用的异步函数表达式
(
async function() {
console.log(await Promise.resolve(3));
}
)();
// 3
await
能够被try...catch...
进行捕获:
async function promise() {
function p1() {
return new Promise((resolve) => {
resolve(1);
});
}
function p2() {
return new Promise((resolve, reject) => {
reject(2);
});
}
function p3() {
return new Promise((resolve, reject) => {
resolve(3);
});
}
try {
var p11 = await p1();
var p22 = await p2();
var p33 = await p3();
console.log(p11);
console.log(p22);
console.log(p33);
} catch (e) {
console.log("发生错误:" + e); // 发生错误:2
}
}
promise();
错误捕获
:::info
1、try...catch...
能捕获await
的异常
2、promise
的异常只能通过.catch
捕获
3、setTimeout
的异常只能在回调函数内进行捕获
:::
1、无法捕获new Promise
里面的错误
function foo() {
try {
return new Promise((resolve, reject) => {
console.log(a);
});
} catch (error) {
// 无法捕获
console.log("error: " + error);
}
}
foo().catch((err) => console.log(err)); // a is not defined
function foo() {
try {
return new Promise((resolve, reject) => {
reject("abc");
});
} catch (error) {
// 无法捕获
console.log("error: " + error);
}
}
foo().catch((err) => console.log(err)); // abc
2、可以捕获到await
async function foo() {
try {
await new Promise((resolve, reject) => {
reject("abc");
});
} catch (error) {
console.log("error: " + error); // error: abc
}
}
foo();
3、无法捕获setTimeout
function foo() {
try {
setTimeout(() => {
console.log(a); // a is not defined
}, 500);
} catch (error) {
// 无法捕获
console.log("error: " + error);
}
}
foo();
相关链接: