异步调用总是创建一个新线程吗?
示例:
如果JavaScript是单线程的,那么它如何进行异步回发呢?它是不是真的被阻塞了,直到它得到回调?如果是这样,这真的是一个异步调用吗?
发布于 2009-03-05 15:14:38
这是一个有趣的问题。
异步编程是一种编程范例,它主要是单线程的,即“遵循一个连续执行的线程”。
您提到的是javascript,所以让我们在web浏览器的环境中讨论一下该语言。web浏览器在每个窗口中运行javascript执行的单线程,它处理事件(如onclick="someFunction()")和网络连接(如xmlhttprequest调用)。
<script>
function performRequest() {
xmlhttp.open("GET", "someurl", true);
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {
alert(xmlhttp.responseText);
}
}
xmlhttp.send(sometext);
}
</script>
<span onclick="performRequest()">perform request</span>
(这是一个不起作用的示例,仅用于演示概念)。
为了以异步的方式做所有的事情,控制线程有一个所谓的“主循环”。主循环看起来像这样:
while (true) {
event = nextEvent(all_event_sources);
handler = findEventHandler(event);
handler(event);
}
需要注意的是,这不是一个“忙循环”。这有点像休眠线程,等待活动发生。活动可以是来自用户的输入(鼠标移动、按钮单击、键入),也可以是网络活动(来自服务器的响应)。
所以在上面的例子中,
从远程服务器返回数据,nextEvent()返回网络事件,发现事件处理程序是onreadystatechange()方法,调用该方法,并启动
值得注意的是alert()是一个阻塞对话框。当该对话框打开时,不能再处理其他事件。这是网页的javascript模型的一个古怪之处,我们有一个现成的方法,可以在该页面的上下文中阻止进一步的执行。
发布于 2009-02-28 10:43:48
Javascript模型是单线程的。异步调用不是新线程,而是中断现有线程。它类似于内核中的中断。
是的,使用单个线程进行异步调用是有意义的。下面是如何思考这一点的:当你在单个线程中调用一个函数时,当前方法的状态被推送到一个堆栈上(即局部变量)。子例程被调用并最终返回,此时从堆栈中弹出原始状态。
使用异步回调,同样的事情也会发生!不同之处在于,子例程由系统调用,而不是由调用子例程的当前代码调用。
发布于 2009-03-06 22:29:11
关于JavaScript有几个特别的注意事项:
默认情况下,XMLHttpRequest
是非阻塞的。在将请求中继到基础网络堆栈之后,send()
方法立即返回。来自服务器的响应将安排在事件循环上调用您的回调,正如其他优秀答案所讨论的那样。
这不需要新的线程。底层socket API是可选择的,类似于Java语言中的java.nio.channels
。
可以通过将false
作为第三个参数传递给open()
来构造同步XMLHttpRequest
对象。这将导致send()
方法阻塞,直到收到来自服务器的响应,从而将事件循环置于网络延迟的支配之下,并可能使浏览器挂起,直到网络超时。这是一件坏事™。
Firefox3.5将通过Worker
类引入真正的多线程JavaScript。后台代码在完全独立的环境中运行,并通过在事件循环上安排回调来与浏览器窗口通信。
https://stackoverflow.com/questions/598436
复制