使用“等待"内部非异步函数 [英] Using "await" inside non-async function

查看:22
本文介绍了使用“等待"内部非异步函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个异步函数,它由我代码中某处的 setInterval 运行.此函数定期更新一些缓存.

I have an async function that runs by a setInterval somewhere in my code. This function updates some cache in regular intervals.

我还有一个不同的同步函数,它需要检索值——最好从缓存中检索,但如果是缓存未命中,则从数据源中检索(我意识到以同步方式进行 IO 操作是不明智的,但让我们假设在这种情况下这是必需的).

I also have a different, synchronous function which needs to retrieve values - preferably from the cache, yet if it's a cache-miss, then from the data origins (I realize making IO operations in a synchronous manner is ill-advised, but lets assume this is required in this case).

我的问题是我希望同步函数能够等待来自异步函数的值,但不可能在非 async 中使用 await 关键字 功能:

My problem is I'd like the synchronous function to be able to wait for a value from the async one, but it's not possible to use the await keyword inside a non-async function:

function syncFunc(key) {
    if (!(key in cache)) {
        await updateCacheForKey([key]);
    }
}

async function updateCacheForKey(keys) {
    // updates cache for given keys
    ...
}

现在,可以通过将 updateCacheForKey 中的逻辑提取到一个新的同步函数中,并从两个现有函数中调用这个新函数来轻松规避这一点.

Now, this can be easily circumvented by extracting the logic inside updateCacheForKey into a new synchronous function, and calling this new function from both existing functions.

我的问题是,为什么首先要绝对阻止这种用例?我唯一的猜测是它与防白痴"有关,因为在大多数情况下,等待来自同步函数的异步函数是错误的.但我认为它有时具有有效的用例是错误的吗?

My question is why absolutely prevent this use case in the first place? My only guess is that it has to do with "idiot-proofing", since in most cases, waiting on an async function from a synchronous one is wrong. But am I wrong to think it has its valid use cases at times?

(我认为这在 C# 中也可以通过使用 Task.Wait 实现,尽管我可能会在这里混淆).

(I think this is possible in C# as well by using Task.Wait, though I might be confusing things here).

推荐答案

我的问题是我希望同步函数能够等待来自异步函数的值...

My problem is I'd like the synchronous function to be able to wait for a value from the async one...

他们不能,因为:

  1. JavaScript 基于作业队列"工作由线程处理,其中作业具有运行到完成语义,并且

  1. JavaScript works on the basis of a "job queue" processed by a thread, where jobs have run-to-completion semantics, and

JavaScript 并没有真正的异步函数——即使 async 函数在幕后也是返回 promise 的同步函数(详情如下)

JavaScript doesn't really have asynchronous functions — even async functions are, under the covers, synchronous functions that return promises (details below)

作业队列(事件循环)在概念上非常简单:当需要完成某件事时(脚本的初始执行、事件处理程序回调等),该工作被放入作业队列中.为该作业队列提供服务的线程接收下一个挂起的作业,运行它直到完成,然后返回进行下一个作业.(当然,它比那更复杂,但这对我们的目的来说已经足够了.) 所以当一个函数被调用时,它被作为工作处理的一部分被调用,并且工作总是被处理到完成在下一个作业可以运行之前.

The job queue (event loop) is conceptually quite simple: When something needs to be done (the initial execution of a script, an event handler callback, etc.), that work is put in the job queue. The thread servicing that job queue picks up the next pending job, runs it to completion, and then goes back for the next one. (It's more complicated than that, of course, but that's sufficient for our purposes.) So when a function gets called, it's called as part of the processing of a job, and jobs are always processed to completion before the next job can run.

运行到完成意味着如果作业调用了一个函数,则该函数必须在作业完成之前返回.当线程跑去做其他事情时,作业不会在中间暂停.这使得代码戏剧性更容易正确编写和推理,而不是在其他事情发生时作业可能在中间暂停.(同样,它比那更复杂,但对于我们这里的目的来说,这已经足够了.)

Running to completion means that if the job called a function, that function has to return before the job is done. Jobs don't get suspended in the middle while the thread runs off to do something else. This makes code dramatically simpler to write correctly and reason about than if jobs could get suspended in the middle while something else happens. (Again it's more complicated than that, but again that's sufficient for our purposes here.)

到目前为止一切顺利.没有真正的异步函数是什么意思?!

So far so good. What's this about not really having asynchronous functions?!

虽然我们谈论的是同步"与异步"函数,甚至有一个 async 关键字我们可以应用于函数,在 JavaScript 中函数调用总是同步.async 函数是一种函数,它同步返回一个承诺,即函数的逻辑实现或拒绝稍后,对环境稍后调用的回调进行排队.

Although we talk about "synchronous" vs. "asynchronous" functions, and even have an async keyword we can apply to functions, a function call is always synchronous in JavaScript. An async function is a function that synchronously returns a promise that the function's logic fulfills or rejects later, queuing callbacks the environment will call later.

让我们假设 updateCacheForKey 看起来像这样:

Let's assume updateCacheForKey looks something like this:

async function updateCacheForKey(key) {
    const value = await fetch(/*...*/);
    cache[key] = value;
    return value;
}

真正在幕后做的是(非常粗略,不是字面意思):

function updateCacheForKey(key) {
    return fetch(/*...*/).then(result => {
        const value = result;
        cache[key] = value;
        return value;
    });
}

(我在我最近的书 JavaScript: The New Toys 的第 9 章中详细介绍了这一点>.)

(I go into more detail on this in Chapter 9 of my recent book, JavaScript: The New Toys.)

它要求浏览器开始获取数据的过程,并在其中注册一个回调(通过then),以便浏览器在数据返回时调用,然后它退出,从then返回promise.尚未获取数据,但 updateCacheForKey 已完成.它已经回来了.它同步完成其工作.

It asks the browser to start the process of fetching the data, and registers a callback with it (via then) for the browser to call when the data comes back, and then it exits, returning the promise from then. The data isn't fetched yet, but updateCacheForKey is done. It has returned. It did its work synchronously.

稍后,当获取完成时,浏览器将作业排队以调用该承诺回调;当从队列中取出该作业时,将调用回调,其返回值用于解析返回的承诺 then.

Later, when the fetch completes, the browser queues a job to call that promise callback; when that job is picked up from the queue, the callback gets called, and its return value is used to resolve the promise then returned.

我的问题是为什么首先要绝对阻止这种用例?

My question is why absolutely prevent this use case in the first place?

让我们看看它会是什么样子:

Let's see what that would look like:

  1. 线程选择一个作业,该作业涉及调用 syncFunc,后者调用 updateCacheForKey.updateCacheForKey 要求浏览器获取资源并返回其承诺.通过这种非异步 await 的魔力,我们同步等待该承诺得到解决,从而阻止了工作.

  1. The thread picks up a job and that job involves calling syncFunc, which calls updateCacheForKey. updateCacheForKey asks the browser to fetch the resource and returns its promise. Through the magic of this non-async await, we synchronously wait for that promise to be resolved, holding up the job.

在某个时刻,浏览器的网络代码完成了对资源的检索,并将作业排队以调用我们在 updateCacheForKey 中注册的承诺回调.

At some point, the browser's network code finishes retrieving the resource and queues a job to call the promise callback we registered in updateCacheForKey.

什么也没发生,再一次.:-)

Nothing happens, ever again. :-)

...因为作业具有 run-to-completion 语义,并且线程在完成前一个作业之前不允许选择下一个作业.不允许线程在中间挂起调用 syncFunc 的作业,以便它可以处理解决承诺的作业.

...because jobs have run-to-completion semantics, and the thread isn't allowed to pick up the next job until it completes the previous one. The thread isn't allowed to suspend the job that called syncFunc in the middle so it can go process the job that would resolve the promise.

这似乎是武断的,但同样,这样做的原因是它使编写正确的代码和推断代码正在做什么变得更加容易.

That seems arbitrary, but again, the reason for it is that it makes it dramatically easier to write correct code and reason about what the code is doing.

但这确实意味着同步"函数不能等待异步"功能完成.

But it does mean that a "synchronous" function can't wait for an "asynchronous" function to complete.

上面有很多很多的细节和诸如此类的挥手.如果你想深入了解它的本质,你可以深入研究规范.带上很多食物和保暖的衣服,你会得到一些时间.:-)

There's a lot of hand-waving of details and such above. If you want to get into the nitty-gritty of it, you can delve into the spec. Pack lots of provisions and warm clothes, you'll be some time. :-)

这篇关于使用“等待"内部非异步函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆