浅谈 async 和 await

您所在的位置:网站首页 吃字是什么结果 浅谈 async 和 await

浅谈 async 和 await

2023-12-06 03:56| 来源: 网络整理| 查看: 265

async 和 await 前言

首先我们先从字面意思来理解。async 是“异步”的简写,而 await 可以认为是 async wait 的简写(就是异步等待)。所以应该很好理解 async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。

async 起什么作用

我们要知道async是干什么的,因为在js中所有任务都是单线程执行的,代码执行顺序都是线性的,如果遇到需要请求接口的时候,代码会一直卡到请求的时候,等带接口完成之后,才会执行下面的代码,这就是线性的,很显然,这种方式并不是很友好,如果其中有一段代码出了问题,而我们没有处理这段异常的话,代码执行就会终止,所以js中引入了一个异步任务的概念。

async 是ES7才有的与异步操作有关的关键字,和 Promise 有很大关联。 (至于Promise是什么,请移步 )

返回值:

async 函数返回一个 Promise 对象,可以使用 .then 方法添加回调函数

例: async function helloAsync(){ return "helloAsync"; } console.log(helloAsync()) // Promise {: "helloAsync"} helloAsync().then(v=>{ console.log(v); // helloAsync })

但是我们往往并不是只用一个async。 而是在async函数中可能会有 await 表达式,async 函数执行时,如果遇到 await 就会先暂停执行,等到触发的异步操作完成后,恢复 async 函数的执行并返回解析值。

注意:

await 关键字仅在 async function 中有效。如果在 async function 函数体外使用 await ,你只会得到一个语法错误。

function testAwait(){ return new Promise((resolve) => { setTimeout(function(){ console.log("testAwait"); resolve(); }, 1000); }); } async function helloAsync(){ await testAwait(); console.log("helloAsync"); } helloAsync(); // testAwait // helloAsync await 到底在等待什么

一般来说,都认为 await 是在等待一个 async 函数完成。不过按语法说明,await 等待的是一个表达式,这个表达式的计算结果是 Promise 对象或者其它值(换句话说,就是没有特殊限定)。 这确保了重用块或将它们从一个地方移动到另一个地方所需的独立性。

这是因为 async 函数返回一个 Promise 对象,所以 await 可以用于等待一个 async 函数的返回值——这也可以说是 await 在等 async 函数,但要清楚,它等的实际是一个返回值。注意到 await 不仅仅用于等 Promise 对象,它可以等任意表达式的结果,所以,await 后面实际是可以接普通方法调用或者直接量的。所以下面这个示例完全可以正确运行

// 这是一个普通方法 function getSomething() { return "something"; } // 这是一个返回Promise的方法 async function testAsync() { return Promise.resolve("hello async"); } async function test() { const v1 = await getSomething(); const v2 = await testAsync(); console.log(v1, v2); } test(); 返回结果

如之前所说,await 会等到后面的 Promise 返回结果 后才会执行 async 函数后面剩下的语句。

async function fn() { console.log(1); await new Promise(function(resolve, reject) { setTimeout(function() { console.log(2); }, 2000); }); console.log(3); } fn(); // 1 // 2 (2 秒后输出,并且后面不会继续输出 3)

那问题就又来了,为什么3并没有输出呢?因为刚才说了await会等到后面的 Promise 返回结果 后才会执行 async 函数后面剩下的语句,而当前的Promise对象并没有返回 resolve 或 reject 。 所以它就一直在等下去,也就一直等不到剩下的语句执行了(还挺痴情,不像有的人路过也不点个赞);

但是如果 await 后面的 Promise 返回一个 reject 状态的结果的话,则会被当成错误在后台抛出。

async function fn() { console.log(1); var result = await new Promise(function(resolve, reject) { setTimeout(function() { reject(2); }, 2000); }); console.log(3); } fn(); // 1 // Uncaught (in promise) 2 (2 秒后输出)

如上结果所示,2 秒后会抛出错误,并且 3 这个数并没有被打印,说明后面的代码也没有执行。 也就是说await语句后面跟着的Promise对象一旦抛出错误,也就是变成reject状态,那么整个async函数就会停止执行。

当然这并不是我们想要的结果,其实我们可以通过对代码进行修改。使值作为一个error对象输出,这样代码就不会报错,但是3依旧没有打印。

async function fn() { console.log(1); var result = await new Promise(function(resolve, reject) { setTimeout(function() { reject(2); }, 2000); }); console.log(3); } fn().catch(function(error) { console.log(error); }); // 1 // 2 (2 秒后输出) 匿名函数

async 也可以用于申明匿名函数用于不同场景,或者嵌套使用 async 函数,如 await async 的形式,只是要在 await 后面使用 async 形式的函数的话,需要这个函数立即执行且有返回值;

let fn = async function() { let a = await (async function() { console.log(1); return 2; })(); console.log(a); async function fn2() { return 3; } console.log(await fn2()); } fn(); // 1 // 2 // 3 回调函数

正常我们在代码里写接口调用的时候,往往是通过.then的链式调用去取值,如果说后续接口用到了前面一个接口的返回值的话,那么我们的代码就会越写越多,缩进也越来越多。这就是我们常说的 JS 的回调地狱

例:

就比如下面这样

First(function (r1) { Second(r1, function (r2) { Third(r2, function (r3) { console.log(r3); }) }) })

解决回调地狱有很多方法,比如:Promise 对象、Generator 函数、async 函数。 这里我们只说 async 方法。

我们只需要将方法修改为 async 方法 并且在里面使用 await 即可

async function request() { const r1 = await First() const r2 = await Second(r1) const r3 = await Third(r2) console.log(r3) // r3 就是我们想要的结果 }

这样写最大的好处就是,代码结构更清晰,有更好的语义,写复杂业务的时候阅读起来更快更爽。

其实 async 和 await 就是为了将异步任务读起来更像是同步代码才诞生的。毕竟 JS 是一个单线程的运行机制。


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3