js程序是构建在事件之上的。输入可能来自不同的外部源。在一些语言中,我们习惯地编写代码来等待某个特定的输入。

var text=downloadSync('http://example.com/file.txt');
console.log(text);

像这样的形式downloadSync称为同步函数(或阻塞函数)。程序会停止做任何工作,而等待它的输入。在这个例子中,也就是等待从网络上下载文件的结果。由于在等待下载完成的期间,计算机可以做其他有用的工作,因此这样的语言通常为程序员提供一种方法来创建多个线程,即并行执行子计算。它允许程序的一部分停下来等待(阻塞)一个低速的输入,而程序的另一部分可以继续进行独立的工作。
在js中,大多的I/O操作都提供了异步的或非阻塞的API。程序员提供一个回调函数,一旦输入完成就可以被系统调用,而不是程序阻塞在等待结果的线程上。

var text=downloadSync('http://example.com/file.txt',function(text){
console.log(text);
});

该API初始化下载进行,然后在内部注册表中存储了回调函数后立即返回,而不是被网络请求阻塞。当下载完成之后,系统会将下载完的文件的文本作为参数调用该已注册的回调函数。
随着事件的发生,事件被添加到应用程序的事件队列的末尾。js系统使用一个内部循环机制来执行应用程序。该循环机制每次都拉取队列底部的事件,以接收到这些事件的顺序来调用这些已经注册的js事件处理程序,并将事件的数据作为该事件处理程序的参数。
运行到完成机制担保的好处是当代码运行时,你完全掌握应用程序的状态。根本不必担心一些变量和对象属性的改变由于并发执行代码而超出你的控制。并发编程在js中往往比使用线程和锁的c++,java或c#更容易。
然而,运行到完成机制的不足是,实际上所有你编写的代码支撑着余下应用程序的继续执行。像浏览器这样的交互式应用程序中,一个阻塞的事件处理程序会阻塞任何将被处理的其他用户输入,甚至可能阻塞一个页面的渲染,从而导致页面失去响应的用户体验。在服务器环境中,一个阻塞的事件处理程序可能会阻塞将被处理的其他网络请求,从而导致服务器失去响应。
js并发的一个最重要的规则是绝不要在应用程序事件队列中使用阻塞I/O的API。在浏览器中,甚至几乎没有任何阻塞API是可用的,尽管有一些平台实现了。提供类似于downloadAsync功能的网络I/O的XMLHttpRequest库有一个同步的版本实现,被认为是不好的。对于web应用程序的交互性,同步的I/O会导致灾难性的后果,它在I/O操作完成之前一直会阻塞用户与页面的交互。
相比之下,异步的API用在基于事件的环境中是安全的,它们迫使应用程序逻辑在一个独立的事件循环“轮询”中继续处理。如上面的例子,假设需要几秒钟来下载网络资源,在这段时间里,数量庞大的其他事件很可能发生。在同步的实现中,这些事件就会堆积在事件队列中,而事件循环将停留等待该JS代码执行完成,这将阻塞任何其他事件的处理。在异步的版本中,JS代码注册一个事件处理程序并立即返回,这将在下载完成之前,允许其他处理程序处理这期间的事件。
在主应用程序事件队列不受影响的环境中,阻塞操作很少出问题。例如,web平台提供了Worker的API,该API使得产生大量的并行计算成为可能。不同于传统的线程执行,Workers在一个完全隔离的状态下执行,没有获取全局作用域或应用程序主线程web页面内容的能力。因此,它们不会妨碍主事件队列中运行的代码的执行。在一个Worker中,使用XMLHttpRequest同步的变种很少出问题。下载操作的确会阻塞Worker继续执行,但这并不会阻止页面的渲染或事件队列中的事件响应。在服务器端环境中,阻塞的API在启动一开始是没有问题的,也就是在服务器开始响应输入的请求之前。然后在处理请求期间,浏览器事件队列中存在阻塞的API就是有问题的啦。

提示

  • 异步API使用回调函数来延缓处理代价高昂的操作以避免阻塞主应用程序

  • js并发地接收事件,但会使用一个事件队列按序地处理事件处理程序

  • 在应用程序事件队列中绝不要使用阻塞的I/O

[Effective JavaScript 笔记]第61条:不要阻塞I/O事件队列的更多相关文章

  1. [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象

    js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...

  2. [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符

    “1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...

  3. [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

    函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...

  4. [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

    js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...

  5. [Effective JavaScript 笔记]第65条:不要在计算时阻塞事件队列

    第61条解释了异步API怎样帮助我们防止一段程序阻塞应用程序的事件队列.使用下面代码,可以很容易使一个应用程序陷入泥潭. while(true){} 而且它并不需要一个无限循环来写一个缓慢的程序.代码 ...

  6. [Effective JavaScript 笔记]第62条:在异步序列中使用嵌套或命名的回调函数

    异步程序的操作顺序 61条讲述了异步API如何执行潜在的代价高昂的I/O操作,而不阻塞应用程序继续处理其他输入.理解异步程序的操作顺序刚开始有点混乱.例如,下面的代码会在打印"finishe ...

  7. [Effective JavaScript 笔记]第67条:绝不要同步地调用异步的回调函数

    设想有downloadAsync函数的一种变种,它持有一个缓存(实现为一个Dict)来避免多次下载同一个文件.在文件已经被缓存的情况下,立即调用回调函数是最优选择. var cache=new Dic ...

  8. [Effective JavaScript 笔记]第68条:使用promise模式清洁异步逻辑

    构建异步API的一种流行的替代方式是使用promise(有时也被称为deferred或future)模式.已经在本章讨论过的异步API使用回调函数作为参数. downloadAsync('file.t ...

  9. [Effective JavaScript 笔记]第46条:使用数组而不要使用字典来存储有序集合

    对象属性无序性 js对象是一个无序属性集合. var obj={}; obj.a=10; obj.b=30; 属性a和属性b并没有谁前谁后之说.for...in循环,先输出哪个属性都有可能.获取和设置 ...

随机推荐

  1. EF实体框架之CodeFirst五

    上一博客学习了下基本的约定配置,留下几个遗漏的,这篇就是学习下遗漏一复杂类型. 一.什么是复杂类型? 书中说道:“复杂类型也可视作值类型(?)可以作为附加属性添加到其他类.复杂类型与实体类型的区别在于 ...

  2. ModernUI教程:第一个ModernUI应用(手动编写)

    这篇文章带着我们从头开始创建一个Modern UI应用.有关采用项目模板和项模板创建Modern UI应用请参看  第一个ModernUI应用(采用项目模板). 1:获取最新的ModernUI发布并解 ...

  3. c#取得控制台应用程序根目录

    1.取得控制台应用程序的根目录方法 方法1.Environment.CurrentDirectory 取得或设置当前工作目录的完整限定路径方法2.AppDomain.CurrentDomain.Bas ...

  4. [AaronYang]C#人爱学不学[4]

    本文章不适合入门,只适合有一定基础的人看.我更相信知识细节见高低,我是从4.0开始学的,终于有时间系统的学习C#5.0,是5.0中的知识,会特殊标记下.但写的内容也可能含有其他版本framework的 ...

  5. github的初次体验及管理代码的心得

    周六早上的课上,助教给我们演示了一遍如何上传和下载代码库,新建代码库等等,但是是在linux上的,而我的笔记本的操作系统是win7的.而在教室中的尝试因为网络原因,虽然可以上github的网站,但是下 ...

  6. hdu1247 字典树

    开始以为枚举会超时,因为有50000的词.后来试了一发就过了.哈哈.枚举没一个单词,将单词拆为2半,如果2半都出现过,那就是要求的. #include<stdio.h> #include& ...

  7. Hibernate-二级缓存策略

    二级缓存的策略 当多个并发的事务同时访问持久化层的缓存中的相同数据时,会引起并发问题,必须采用必要的事务隔离措施. 在进程范围或集群范围的缓存,即第二级缓存,会出现并发问题.因此可以设定以下4种类型的 ...

  8. BZOJ-1951 古代猪文 (组合数取模Lucas+中国剩余定理+拓展欧几里得+快速幂)

    数论神题了吧算是 1951: [Sdoi2010]古代猪文 Time Limit: 1 Sec Memory Limit: 64 MB Submit: 1573 Solved: 650 [Submit ...

  9. 【poj1006】 Biorhythms

    http://poj.org/problem?id=1006 (题目链接) 题意 人自出生起就有体力,情感和智力三个生理周期,分别为23,28和33天.一个周期内有一天为峰值,在这一天,人在对应的方面 ...

  10. POJ 2240Arbitrage(Floyd)

    E - Arbitrage Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submi ...