回调函数
ajax请求
ajax("http://some.url.1", function myCallbackFunction(data) {
console.log( data );
});
事件循环
var eventLoop = [ ];
var event;
while(true) {
//一次tick
if(eventLoop.length > 0){
event = eventloop.shift();
try {
event();
}
catch (err) {
reportError(err);
}
}
}
有一个用while循环实现的持续运行的循环,循环的每一轮称为一个tick.对每个tick而言,如果在队列中有等待事件,那么就会从队列中摘下一个事件并执行。这些事件就是你的回调函数。
一定要清楚,setTimeout(..)并没有把你的回调函数挂在事件循环队列中。它所做的是设定一个定时器。当定时器到时后,环境会把你的回调函数放在事件循环中,这样,在未来某个时刻的tick会摘下并执行这个回调。
并行线程
异步和并行的意义完全不同,但它们的意义却时常被混淆。
异步是关于现在和将来的时间间隙,而并行是关于能够同时发生的事
function later() {
answer = answer* 2;
console.log("Meaning of life:", answer);
}
单线程执行的话,上面的代码不会有任何疑问。
但如果是多线程编程,结果却并不是那么的确定。虽然javascript从来不跨线程共享数据,但这并不意味这javascript执行的结果是确定的。不同的执行顺序,可能得到不同的结果。
var a = 20;
function foo() {
a = a + 1;
}
function bar() {
a = a + 2;
}
ajax("http://some.url.1",foo );
ajax("http://some.url.2", bar );
完整运行
由于javascript的单线程特性,foo(),bar() 中的代码具有原子性 ,即函数内部的代码是一起运行的,不可分割的。这就称为完整运行。尽管还是会因为函数运行的顺序的不同可能产生不同的结果。但不会因为多线程出现问题,确定性也高很多。
在javascript的特性中。这种函数顺序的不确定性就是通常所说的竞态条件。
并发
两个进程同时执行就出现了并发
非交互
两个或多个进程在同一个程序内并发的交替运行它们的步骤,如果这些任务彼此不相关,就不一定需要交互
var res = {};
function foo(results) {
res.foo = results;
}
function bar(results) {
res.bar = results;
}
ajax("http://some.url.1",foo);
ajax("http://some.url.2",bar);
foo() 和 bar() 是两个并发执行的进程,按照什么顺序执行是并不确定的。但是无论怎么运行都不会有影响
交互
更常见的情况是,并发的进程需要相互交流,通过作用域与DOM间接交互,那么这样的话就需要对它们的交互进行协调以避免竞态的出现
var res = {};
function response(data) {
res.push(data);
}
ajax("http://some.url.1", response);
ajax("http://some.url.2", response);
我们不会知道到底哪个ajax请求会率先完成,这对我们的程序造成了极大的不确定性。所以我们会想办法解决这些问题,重新组织我们的代码
var res = [];
function response(data) {
if(data.url == "http://some.url.1") {
res[0] = data;
}
if(data.url == "http://some.url.2") {
res[1] = data;
}
}
ajax("http://some.url.1", response );
ajax("http://some.url.2", response );
协作
还有一种并发合作方式,称为并发协作。这里的重点不再是通过共享作用域中的值进行交互。这里的目标是取得一个长期运行的进程,并将其分割成多个步骤或多批任务,使得其他并发进程有机会将自己的运算插入到事件循环队列中交替运行。
一个实例
var res = [];
function response(data) {
res = res.concat(
data.map( function(val) {
return val * 2;
}
)
);
}
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );
如果上面的data列表特别大的话,这一过程将会花费较长的时间。如果几秒甚至十秒界面不能进行任何操作,将非常影响用户体验,所以就将一个大的任务分解成若干个小任务,就可以在这些间隙执行用户的操作。
solution:
var res = [];
function response(data) {
var chunk = data.splice( 0, 1000); //一次处理1000个
res = res.concat(
chunk.map( function(val) {
return val * 2;
})
);
if(data.length > 0) {
setTimeout( function() {
response( data );
},0);
}
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );