为什么需要async在JavaScript函数体内调用wait



必须在包含函数上使用async关键字,才能在函数体内使用等待

async function fetchMovies() {
const response = await fetch('/movies');
console.log(response);
}
fetchMovies();

wait用于在异步fetch()调用完成时进行阻塞。从代码中可以看出,函数fetchMovies()甚至没有返回任何值。即使是这样,它也会影响调用者使用返回值的方式,但为什么这对函数体对另一个异步调用的调用很重要呢?

我的问题是为什么需要这样做?对此有什么好的解释吗?它是否与实际实现wait并在旧的JavaScript版本中支持它的需求有关?

我知道使用iffi模式可以使用等待,但这会以任何方式改变ifi

(async () => {
const response = await fetch('/movies');
console.log(response);
})();

我还知道模块中支持顶级等待

可能是我错过了一些非常明显的东西。

async关键字存在有三个原因:

  1. 在2015年之前的ECMAScript语言版本中,await不是关键字。标记函数CCD_;救助;以指示函数体内语言语法的突破性变化。

    这是最重要的原因。如果没有async关键字,如果使用await关键字作为变量(事实上,在async/await标准化之前,在某些情况下,这是故意作为polyfill进行的),那么用ECMAScript 5或更早版本编写的所有程序都将不再工作,因为如果不在规范中添加async,这将导致中断性的更改。因此,async在语法上是必要的,以避免破坏对语言的更改

  2. 它为解析器提供了一个方便的标记,避免了为了确定函数是否异步而进行的无限前瞻。

    这使得解析更加高效,这对ECMAScript实现者和开发人员都很有吸引力,尽管仅凭这一原因并不能使async对语法来说是严格必要的。

  3. async也对函数执行自己的转换,无论正文中是否存在await关键字都会执行转换。

    考虑以下两个功能:

    function foo() {
    if (Math.random() < 0.5) {
    return 'return';
    } else {
    throw 'throw';
    }
    }
    async function bar() {
    if (Math.random() < 0.5) {
    return 'return';
    } else {
    throw 'throw';
    }
    }
    

    asyncfunction bar()执行以下转换:

    function bar() {
    return new Promise((resolve, reject) => {
    try {
    resolve((/*async function bar*/() => {
    if (Math.random() < 0.5) {
    return 'return';
    } else {
    throw 'throw';
    }
    })());
    } catch (reason) {
    reject(reason);
    }
    });
    }
    

    熟悉Promise的人会认识到,我们可以简化上面的内容,因为如果Promise构造函数executor函数同步抛出错误,它将隐式拒绝:

    function bar() {
    return new Promise((resolve) => {
    if (Math.random() < 0.5) {
    return resolve('return');
    } else {
    throw 'throw';
    }
    });
    }
    

这里有两个问题。为什么需要async关键字来指示异步上下文,为什么需要asynccontext来使用await?

如果我们不使用async关键字来指示异步上下文,那么我们将存在向后兼容性问题。在异步/等待更新之前;等待";是一个有效的变量名。因此,为了使它成为一个保留词,我们需要引入一个新的上下文,异步函数上下文。

第二个问题是,我们为什么要这样做?为什么我们要将异步函数与传统的同步代码区分开来?顾名思义,异步函数不会同时执行所有代码。当函数";等待";,它有效地停止了执行,代码的其余部分变成了正在等待的事情的事件处理程序。这允许运行其他代码。

浏览器javascript实现是单线程的。一次只能执行一件事,但事件队列允许函数轮流执行。考虑一下,如果您可以从同步函数等待,会发生什么。根据定义,同步代码不会放弃控制。因此,它正在等待的异步函数将永远不会有机会切换到您的单个执行线程,您将永远等待。因此,只有在已经处于异步上下文中的情况下,才能等待异步函数。

我想你的确切问题是:">处理返回值(null或其他值)取决于调用函数的使用者。他们";应该";即使在两者之间调用了另一个异步函数,也应该得到它。那么,为什么在进行其他异步调用之前等待很重要呢";

你看,这样的fetch()调用是在数据库中在"0"的持续时间内完成的;启动";以及";关闭";连接。在这种情况下,几乎所有使用的方法都是异步的。因此,在执行fetchMovies();时,线程可能会进一步移动,并在解析和返回获取之前执行connection.close();

具体情况如下:

await client.connect();  //connection establishment
// Your purposeful methods
async function fetchMovies() {
const response = await fetch('/movies');
console.log(response);
}
await fetchMovies();
// Closing connection to avoid attacks and maintain concurrency
await client.close();

在这种情况下,如果以异步方式调用任何方法,则数据库连接的整个会话都将被浪费,并且我们的函数将返回undefined或抛出错误"em";无法使用已结束的会话">

因此,我们需要";等待";对于";挂起";承诺达成";实现了";或";被拒绝";状态,然后再执行进一步的调用。

您可以更多地参考本文:使用Promises,异步/等待MongoDB只是为了便于理解。

我认为这是为了明确该函数包含异步代码。让我们使用另一个返回某些内容的示例:

async function canUseGeolocation() {
const result = await navigator.permissions.query({name: 'geolocation'});
return result.state;
}

async关键字告诉javascript引擎,函数应该返回一个promise,函数中的任何返回语句都应该解析该promise。如果我们修改它以缓存值,这样我们就不会总是调用wait,会发生什么?

function canUseGeolocation() {
if (cachedPermissionState) return cachedPermissionState;
const result = await navigator.permissions.query({name: 'geolocation'});
cachedPermissionState = result.state;
return result.state;
}

javascript应该如何知道函数应该返回promise?因为它包含一个await?如果您稍后更改函数,使cachedPermissionState设置在其他位置,而此函数仅返回该值,从而删除await,该怎么办?第一个return语句应该返回promise还是返回值?这将改变函数的整个执行方式以及return cachedPermissionState;返回的内容。通过使用async,我们可以知道它确实返回了一个promise,return语句解析了这个promise,而无需扫描函数中的await语句来确定它是否应该被视为async

最新更新