我的问题是我希望同步函数能够等待异步函数返回值......但它们不能,因为:
1. JavaScript基于线程处理“作业队列”,其中作业具有完成语义。
2. JavaScript实际上没有异步函数——即使是async函数,在内部也是返回promise的同步函数。
作业队列(事件循环)的概念非常简单:当需要执行某些任务时(脚本的初始执行、事件处理程序回调等),该工作被放入作业队列中。服务于该作业队列的线程会获取下一个挂起的作业,运行到完成,并返回下一个作业。所以,当调用函数时,它是作业处理的一部分,作业总是在下一个作业运行之前处理完成。
处理完成意味着如果作业调用了函数,则该函数必须在作业完成之前返回。作业不会在线程运行其他操作时中途暂停。这使得编写正确且易于理解的代码比允许作业在进行其他操作时中途暂停要简单得多。因此,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;
});
}
我在我的最新书的第9章中详细讨论了这个问题,书名为JavaScript: The New Toys。
它要求浏览器开始获取数据的过程,并通过then
向其注册回调以便在数据返回时进行调用,然后退出,并从then
返回承诺。数据尚未被获取,但是updateCacheForKey
已完成。它已返回。它同步地完成了它的工作。
稍后,当获取完成时,浏览器会排队调用该承诺回调的任务;当从队列中选择该任务时,回调将被调用,并且其返回值将用于解析then
返回的承诺。
我的问题是为什么要在第一时间完全阻止这种使用情况?
让我们看看那会是什么样子:
线程接收到一个工作,其中涉及调用syncFunc
,该函数调用updateCacheForKey
。 updateCacheForKey
请浏览器获取资源并返回它的承诺。通过这种非异步await
的魔法,我们同步等待该承诺被解析,阻止工作的进行。
某个时刻,浏览器的网络代码完成了检索资源的工作,并排队调用我们在updateCacheForKey
中注册的承诺回调的任务。
然后,永远不会再有任何事情发生。 :-)
因为任务具有运行至完成的语义,线程不允许在完成前一个任务之前选择下一个任务。线程不允许在调用
syncFunc
的任务中间挂起该任务,以便处理将解决承诺的任务。
这似乎是武断的,但再次强调,其原因在于它使编写正确代码和理解代码正在执行的内容变得极为容易。
但这意味着“同步”函数无法等待“异步”函数完成。
上面的细节和手势非常多。如果您想深入了解详情,可以查看规范。带上很多衣物和温暖的衣服,您需要花费一些时间。:-)
await
,根据定义,该函数就是异步的。为什么不将async
添加到syncFunc
中呢?或者您是想让syncFunc
在等待期间阻塞线程吗? - Mark