异步调用总是创建一个新线程吗?
例:
如果JavaScript是单线程的,那么它如何进行异步回发呢?它是否实际阻塞,直到它得到回调?如果是这样,这真的是异步电话吗?
这是个有趣的问题.
异步编程是编程的范例,主要是单线程,即"跟随一个连续执行的线程".
您可以参考javascript,因此我们在Web浏览器的环境中讨论该语言.Web浏览器在每个窗口中运行单个javascript执行线程,它处理事件(例如onclick ="someFunction()")和网络连接(例如xmlhttprequest调用).
perform request
(这是一个非工作的例子,仅用于演示概念).
为了以异步方式完成所有操作,控制线程具有所谓的"主循环".主循环看起来像这样:
while (true) { event = nextEvent(all_event_sources); handler = findEventHandler(event); handler(event); }
重要的是要注意,这不是一个"繁忙的循环".这有点像睡觉的线程,等待活动发生.可以从用户输入活动(鼠标移动,按钮单击,键入),也可以是网络活动(来自服务器的响应).
所以在上面的例子中,
当用户单击span时,将生成ButtonClicked事件,findEventHandler()将在span标记上找到onclick事件,然后将使用该事件调用该处理程序.
创建xmlhttp请求时,会将其添加到all_event_sources事件源列表中.
在performRequest()函数返回后,mainloop正在waitEvent()步骤等待响应.在这一点上,没有任何"阻止"进一步的事件被处理.
数据从远程服务器返回,nextEvent()返回网络事件,发现事件处理程序是onreadystatechange()方法,调用该方法,并触发alert()对话框.
值得注意的是,alert()是一个阻塞对话框.该对话框启动时,无法处理其他事件.这是网页javascript模型的一个怪癖,我们有一个随时可用的方法,它将阻止在该页面的上下文中进一步执行.
Javascript模型是单线程的.异步调用不是新线程,而是中断现有线程.它类似于内核中的中断.
是的,使用单个线程进行异步调用是有意义的.以下是如何思考它:当您在单个线程中调用函数时,当前方法的状态被推送到堆栈(即局部变量).子程序被调用并最终返回,此时原始状态从堆栈中弹出.
通过异步回调,同样的事情发生了!不同之处在于子程序是由系统调用的,而不是由调用子程序的当前代码调用的.
特别关于JavaScript的一些注意事项:
XMLHttpRequest
s默认情况下是非阻塞的.send()
在将请求中继到底层网络堆栈之后,该方法立即返回.来自服务器的响应将安排在事件循环上调用您的回调,正如其他优秀答案所讨论的那样.
这不需要新线程.底层套接字API是可选择的,类似于java.nio.channels
Java.
可以通过传递第三个参数来构造同步 XMLHttpRequest
对象.这将导致该方法阻塞,直到从服务器收到响应,从而使事件循环受网络延迟的影响并可能挂起浏览器直到网络超时.这是Bad Thing™.false
open()
send()
Firefox 3.5将在Worker
课堂上引入诚实的多线程JavaScript .后台代码在完全独立的环境中运行,并通过调度事件循环上的回调与浏览器窗口进行通信.