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(); // 1console.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();
相关链接:
