Javascript 使用;等待“;内部非异步函数

Javascript 使用;等待“;内部非异步函数,javascript,asynchronous,async-await,Javascript,Asynchronous,Async Await,我有一个异步函数,它在代码中的某个地方按setInterval运行。此函数定期更新一些缓存 我还有一个不同的同步函数,它需要检索值——最好是从缓存中检索,但是如果是缓存未命中,则从数据源检索 (我意识到以同步方式进行IO操作是不明智的,但假设在这种情况下需要这样做) 我的问题是,我希望同步函数能够等待来自异步函数的值,但不可能在非async函数中使用wait关键字: function syncFunc(key) { if (!(key in cache)) { await

我有一个异步函数,它在代码中的某个地方按setInterval运行。此函数定期更新一些缓存

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

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

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

async function updateCacheForKey(keys) {
    // updates cache for given keys
    ...
}
现在,通过将
updateCacheForKey
中的逻辑提取到一个新的同步函数中,并从两个现有函数调用这个新函数,可以很容易地避免这种情况

我的问题是为什么一开始就绝对阻止这个用例?我唯一的猜测是它与“傻瓜证明”有关,因为在大多数情况下,等待同步函数的异步函数是错误的。但我认为它有时有其有效的用例,这是错误的吗

(我认为在C#中也可以通过使用
Task.Wait
,尽管我可能会把这里的事情弄糊涂)

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

他们不能,因为:

  • JavaScript基于线程处理的“作业队列”工作,其中作业具有运行到完成的语义,并且

  • JavaScript并没有真正的异步函数(真的-请坚持我的观点…)

  • 作业队列(事件循环)在概念上非常简单:当需要执行某些操作(脚本的初始执行、事件处理程序回调等)时,该工作被放入作业队列中。为该作业队列提供服务的线程将拾取下一个挂起的作业,将其运行到完成,然后返回到下一个作业。(当然,这比这更复杂,但就我们的目的而言,这已经足够了。)因此,当调用函数时,它会作为作业处理的一部分被调用,作业总是在下一个作业运行之前被处理到完成

    运行到完成意味着如果作业调用了函数,则该函数必须在作业完成之前返回。当线程运行以做其他事情时,作业不会被挂在中间。这使得代码写得更简单、更合理,而不是在其他事情发生的情况下,作业可以挂在中间。(同样,它比这更复杂,但对于我们这里的目的来说,这已经足够了。)

    到目前为止还不错。没有异步函数是怎么回事

    虽然我们讨论了“同步”与“异步”函数,甚至有一个
    async
    关键字可以应用于函数,但函数调用在JavaScript中始终是同步的。异步函数实际上并不存在。我们有同步函数,可以设置环境稍后将调用的回调(通过对作业排队),如果合适的话

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

    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;
        });
    }
    
    它要求浏览器开始提取数据的过程,并向其注册回调(通过
    然后
    ),以便浏览器在数据返回时调用,然后退出,从
    然后
    返回承诺。数据尚未提取,但
    updateCacheForKey
    已完成。它回来了。它的工作是同步进行的

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

    我的问题是为什么一开始就绝对阻止这个用例

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

  • 线程拾取一个作业,该作业涉及调用
    syncFunc
    ,调用
    updateCacheForKey
    updateCacheForKey
    要求浏览器获取资源并返回其承诺。通过这种非异步
    wait
    的魔力,我们同步地等待承诺得到解决,从而拖延了工作

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

  • 再也不会发生任何事情了。:-)

  • …因为作业已运行到完成语义,线程在完成前一个作业之前不允许拾取下一个作业。线程不允许在中间挂起名为>代码> SycFunc < /C>的作业,这样它就可以处理将要解决的任务。

    这似乎是武断的,但再次强调,其原因是它使编写正确的代码变得非常容易,并说明代码正在做什么

    但这确实意味着“同步”函数不能等待“异步”函数完成

    上面有很多细节和类似的东西。如果你想深入了解它的本质,你可以深入研究它的规格。带上大量的食物和暖和的衣服,你会有一段时间的。:-)


    您可以通过以下方式从非异步函数中调用异步函数:

    如你的例子所示:

    function syncFunc(key) {
       if (!(key in cache)) {
          (async () => await updateCacheForKey([key]))();
       }
    }
    
    async function updateCacheForKey(keys) {
       // updates cache for given keys
       ...
    }
    
    现在,通过将updateCacheForKey内部的逻辑提取到一个新的同步函数中,并从两个现有函数调用这个新函数,可以很容易地避免这种情况

    完美地解释了JavaScript中异步函数的语义。但在我看来,上述段落值得更多讨论。根据
    updateCacheForKey
    的功能,可能无法提取其逻辑i
    function syncFunc(key) {
       if (!(key in cache)) {
          (async () => await updateCacheForKey([key]))();
       }
    }
    
    async function updateCacheForKey(keys) {
       // updates cache for given keys
       ...
    }