Item 61: Don’t Block the Event Queue on I/O

Item 61:   Don’t Block the Event Queue on I/O

JavaScript programs are structured around events: inputs that may 
come in simultaneously from a variety of external sources, such as 
interactions from a user (clicking a mouse button, pressing a key, or 
touching a screen), incoming network data, or scheduled alarms. In 
some languages, it’s customary to write code that waits for a particu-
lar input:

var  text  =  downloadSync("http://example.com/file.txt"); 

console.log(text); 

(The console.log API is a common utility in JavaScript platforms for 
printing  out  debugging  information  to  a  developer  console.)  Func-
tions such as downloadSync are known as synchronous, or blocking: 
The program stops doing any work while it waits for its input—in this 
case, the result of downloading a file over the internet. Since the com-
puter could be doing other useful work while it waits for the download 
to complete, such languages typically provide the programmer with 
a way to create multiple threads: subcomputations that are executed 
concurrently, allowing one portion of the program to stop and wait 
for (“block on”) a slow input while another portion of the program can 
carry on usefully doing independent work.

In  JavaScript,  most  I/O  operations  are  provided  through  asynchronous, or nonblocking APIs. Instead of blocking a thread on a result, the programmer provides a callback  (see Item  19) for the system to invoke once the input arrives:

downloadAsync("http://example.com/file.txt",  function(text)  {
    console.log(text);
}); 

Rather than blocking on the network, this API initiates the download process and then immediately returns after storing the callback in an internal registry. At some point later, when the download has completed, the system calls the registered callback, passing it the text of the downloaded file as its argument.

Now, the system does not just jump right in and call the callback the instant the download completes. JavaScript is sometimes described as providing a run-to-completion guarantee: Any user code that is currently running in a shared context, such as a single web page in a browser, or a single running instance of a web server, is allowed to finish executing before the next event handler is invoked. In effect, the system maintains an internal queue of events as they occur, and invokes any registered callbacks one at a time.

Figure 7.1 shows an illustration of example event queues in client-side 
and server-side applications. As events occur, they are added to the 
end of the application’s event queue  (at the top of the diagram). The 
JavaScript  system  executes  the  application  with  an  internal  event 
loop, which plucks events off of the bottom of the queue—that is, in the 
order in which they were received—and calls any registered Java Script 
event handlers (callbacks like the one passed to downloadAsync above) 
one at a time, passing the event data as arguments to the handlers.

Figure 7.1  Example event queues in a) a web client application and

b) a web server

The benefit of the run-to-completion guarantee is that when your code runs, you know that you have complete control over the application state: You never have to worry that some variable or object property will change out from under you due to concurrently executing code. This has the pleasant result that concurrent programming in JavaScript tends to be much easier than working with threads and locks in languages such as C++, Java, or C#.

Conversely,  the  drawback  of  run-to-completion  is  that  any  and  all 
code  you  write  effectively  holds  up  the  rest  of  the  application  from 
proceeding.  In  interactive  applications  like  the  browser,  a  blocked 
event handler prevents any other user input from being handled and 
can even prevent the rendering of a page, leading to an unresponsive 
user experience. In a server setting, a blocked handler can prevent 
other network requests from being handled, leading to an unrespon-
sive server.

The single most important rule of concurrent JavaScript is never to 
use  any  blocking  I/O  APIs  in  the  middle  of  an  application’s  event 
queue. In the browser, hardly any blocking APIs are even available, 
although a few have sadly leaked into the platform over the years. 
The XMLHttpRequest library, which provides network I/O similar to the 
downloadAsync function above, has a synchronous version that is con-
sidered bad form. Synchronous I/O has disastrous consequences for 
the interactivity of a web application, preventing the user from inter-
acting with a page until the I/O operation completes.

By contrast, asynchronous APIs are safe for use in an event-based set-
ting, because they force your application logic to continue processing 
in a separate “turn” of the event loop. In the examples above, imagine 
that it takes a couple of seconds to download the URL. In that time, 
an enormous number of other events may occur. In the synchronous 
implementation, those events would pile up in the event queue, but 
the event loop would be stuck waiting for the JavaScript code to finish 
executing, preventing the processing of any other events. But in the 
asynchronous version, the JavaScript code registers an event handler 
and  returns  immediately,  allowing  other  event  handlers  to  process 
intervening events before the download completes.

In settings where the main application’s event queue is unaffected, 
blocking operations are less problematic. For example, the web plat-
form  provides  the  Worker  API,  which  makes  it  possible  to  spawn 
concurrent   computations.   Unlike   conventional   threads,   workers 
are  executed  in  a  completely  isolated  state,  with  no  access  to  the 
global scope or web page contents of the application’s main thread, 
so they cannot interfere with the execution of code running in from the main event queue. In a worker, using the synchronous variant of XMLHttpRequest is less problematic; blocking on a download does prevent the Worker from continuing, but it does not prevent the page from rendering or the event queue from responding to events. In a server setting,  blocking  APIs  are  unproblematic  during  startup,  that  is, before the server begins responding to incoming requests. But when servicing requests, blocking APIs are every bit as catastrophic as in the event queue of the browser.

Things to Remember

? Asynchronous APIs take callbacks to defer processing of expensive operations and avoid blocking the main application.

? JavaScript  accepts  events  concurrently  but  processes  event  handlers sequentially using an event queue.

? Never use blocking I/O in an application’s event queue.

文章来源于:Effective+Javascript编写高质量JavaScript代码的68个有效方法 英文版

时间: 2024-10-07 02:57:29

Item 61: Don’t Block the Event Queue on I/O的相关文章

Effective JavaScript Item 61 不要阻塞事件队列

JavaScript处理并发事件的机制是十分友好和强大的,它结合了事件队列(Event Queue)/事件循环并发(Event-loop Concurrency)和一套异步调用API.这因为这一点,JavaScript不仅可以在浏览器环境中运行,还可以在桌面应用和服务器应用中运行,如Node.js. 令人奇怪的是,ECMAScript标准时至今日对并发这个问题还是只字未提.所以以下提到的各种并发方面的注意事项只是基于JavaScript本身语言特性总结得到的. JavaScript程序围绕事件进

gem5中event queue运行原理机制详细分析

搞清楚这个花了两天时间,同时为了给自己赚点下载用的积分,如需要详细问自己介绍版本,请点击下载点击打开链接 图1 与Event相关的类 以上是在gem5中,event相关的类继承图,SimObject.EventBase是Event的基础类.GlobalEvent继承于Event类. (1)Event:gem5中所有和时序相关的操作都是由event来驱动的,比如tick.trap.writeback等等.Event是event queue中的node,任何需要使用Event作为基类的子类需要重构虚

61 (OC)* 代理 block 通知 代理 kvo

1.从源头上理解和区别block和delegate delegate运行成本低,block的运行成本高. block出栈需要将使用的数据从栈内存拷贝到堆内存,当然对象的话就是加计数,使用完或者block置nil后才消除.delegate只是保存了一个对象指针,直接回调,没有额外消耗.就像C的函数指针,只多做了一个查表动作. 2.从使用场景区别block和delegate 有多个相关方法.假如每个方法都设置一个 block, 这样会更麻烦.而 delegate 让多个方法分成一组,只需要设置一次,

LF模式是个坑,ZeroIce中间件让你体会这个痛

LF模式是个坑,一个小小的失误就可能使你的网络处理瘫痪,Ice就很好地展现了出来,换句话说,Ice中间件或是LF模式就是一个坑,如果你一不小心. LF模式的官方论文中,论述了此模式用于高性能网络并发模式,使用的是系统的隐式队列,也就是Reactor复用多路IO,(如果是select的话,还是会将事件收集到一个显式队列),每次只有一条线程可以有一次机会成为leader访问这个队列,从队列取出事件后放弃leader,同时唤醒另一线程(如果还有follower线程的话):注意这时的线程既不是leade

13 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件  queue队列 生产者消费者模型 Queue队列 开发一个线程池

本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者消费者模型 Queue队列 开发一个线程池 进程 语法 进程间通讯 进程池 操作系统发展史 手工操作(无操作系统) 1946年第一台计算机诞生--20世纪50年代中期,还未出现操作系统,计算机工作采用手工操作方式. 手工操作程序员将对应于程序和数据的已穿孔的纸带(或卡片)装入输入机,然后启动输入机把

在Golang中实现有无限容量的同步Queue

chan对象是Golang的一个核心卖点,可以轻松实现goroutine之间的通信.Golang允许我们为chan设置不同的缓冲大小.当默认缓冲大小为0的时候,一个goroutine对chan的写入操作必须要等到有其他goroutine对chan进行读取的时候才会返回,反之一个goroutine对chan进行读取的时候要等到另外一个goroutine对chan进行写入才会返回.如果我们不希望每次对chan进行读取和写入都堵塞的话,可以对chan设置缓冲大小.这样,在缓冲区没满之前,gorouti

python开发线程:死锁和递归锁&信号量&定时器&线程queue&事件evevt

一 死锁现象与递归锁 进程也有死锁与递归锁,在进程那里忘记说了,放到这里一切说了额 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁 from threading import Thread,Lock import time mutexA=Lock() mutexB=Lock() class MyThread(Thread):

Python的Queue模块

1 NAME 2 Queue - A multi-producer, multi-consumer queue. 3 4 CLASSES 5 Queue 6 LifoQueue 7 PriorityQueue 8 exceptions.Exception(exceptions.BaseException) 9 Empty 10 Full 11 12 class Empty(exceptions.Exception) 13 | Exception raised by Queue.get(block

进击的Python【第九章】:paramiko模块、线程与进程、各种线程锁、queue队列、生产者消费者模型

一.paramiko模块 他是什么东西? paramiko模块是用python语言写的一个模块,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接. 先来个实例: 1 import paramiko 2 # 创建SSH对象 3 ssh = paramiko.SSHClient() 4 5 # 允许连接不在know_hosts文件中的主机 6 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 7 # 连接服务器 8 ss