有一种并发合作方式,称为并发协作(cooperative concurrency)。这里的重点不再是通过
共享作用域中的值进行交互(尽管显然这也是允许的!)。这里的目标是取到一个长期运
行的“进程”,并将其分割成多个步骤或多批任务,使得其他并发“进程”有机会将自己
的运算插入到事件循环队列中交替运行。
举例来说,考虑一个需要遍历很长的结果列表进行值转换的 Ajax 响应处理函数。我们会
使用 Array#map(..) 让代码更简洁:
var res = []; // response(..)从Ajax调用中取得结果数组 function response(data) { // 添加到已有的res数组 res = res.concat( // 创建一个新的变换数组把所有data值加倍 data.map( function(val){ return val * 2; } ) ); } // ajax(..)是某个库中提供的某个Ajax函数 ajax( "http://some.url.1", response ); ajax( "http://some.url.2", response );
如果 "http://some.url.1" 首先取得结果,那么整个列表会立刻映射到 res 中。如果记录
有几千条或更少,这不算什么。但是如果有像 1000 万条记录的话,就可能需要运行相当
一段时间了(在高性能笔记本上需要几秒钟,在移动设备上需要更长时间,等等)。
这样的“进程”运行时,页面上的其他代码都不能运行,包括不能有其他的 response(..)
调用或 UI 刷新,甚至是像滚动、输入、按钮点击这样的用户事件。这是相当痛苦的。
所以,要创建一个协作性更强更友好且不会霸占事件循环队列的并发系统,你可以异步地
批处理这些结果。每次处理之后返回事件循环,让其他等待事件有机会运行。
这里给出一种非常简单的方法:
var res = []; // response(..)从Ajax调用中取得结果数组 function response(data) { // 一次处理1000个 var chunk = data.splice( 0, 1000 ); // 添加到已有的res组 res = res.concat( // 创建一个新的数组把chunk中所有值加倍 chunk.map( function(val){ return val * 2; } ) ); // 还有剩下的需要处理吗? if (data.length > 0) { // 异步调度下一次批处理 setTimeout( function(){ response( data ); }, 0 ); } } // ajax(..)是某个库中提供的某个Ajax函数 ajax( "http://some.url.1", response ); ajax( "http://some.url.2", response );
我们把数据集合放在最多包含 1000 条项目的块中。这样,我们就确保了“进程”运行时
间会很短,即使这意味着需要更多的后续“进程”,因为事件循环队列的交替运行会提高
站点 /App 的响应(性能)
摘自《你不知道的javascript(中)》P155