正如动静是相对的概念,有了它们,世界才充满盎然生气;变和不变也是哲学上的对立统一,在代码的世界里也一样;同步异步呢?
首先,来粗略地看看同步和异步各自有些什么好处:
同步的好处:
- 1、同步流程对结果处理通常更为简单,可以就近处理。
- 2、同步流程对结果的处理始终和前文保持在一个上下文内。
- 3、同步流程可以很容易捕获、处理异常。
- 4、同步流程是最天然的控制过程顺序执行的方式。
异步的好处:
- 1、异步流程可以立即给调用方返回初步的结果。
- 2、异步流程可以延迟给调用方最终的结果数据,在此期间可以做更多额外的工作,例如结果记录等等。
- 3、异步流程在执行的过程中,可以释放占用的线程等资源,避免阻塞,等到结果产生再重新获取线程处理。
- 4、异步流程可以等多次调用的结果出来后,再统一返回一次结果集合,提高响应效率。
接下来,我不妨说一些同步和异步互相转化的故事。
先来看看这一段代码:
代码语言:javascript复制setTimeout(function(){
while(true){
alert("In");
}
},0);
while(true){
alert("Out");
}
它的输出应该是怎样的呢?你可以试一试。
不同浏览器下它的表现不同,有的浏览器下一直显示 In 的弹出框;有的浏览器下一直显示 Out 的弹出框;还有的浏览器下先显示一个 Out,再不断显示 In 的弹出框。
那是不是可以这样理解:
上面的代码本意是想描述一个页面的 JavaScript 代码进行类似于并行线程的执行(setTimeout 调用的方法,似乎就是一个异步执行的方法,它本意是不阻止主流程的执行的),可是实际运行的结果发现,原来浏览器运行 JavaScript,所谓的异步,只是对开发人员和用户的一个欺骗,世界只是看起来这个样子—— 实际上,在 JavaScript 的世界里,其实根本就是“ 单线程” 的嘛!
其实,这是无所谓欺骗的,就如同对于单 CPU 的机器来说,所谓的多进程,其实也只有一个 CPU 轮流执行队列里的指令。只是这个世界本来就是那么残酷,也许是我们都看错了……
同步 Ajax 和异步 Ajax
Ajax 通常都是异步的,同步的 Ajax 调用会将浏览器当前页面挂起,拒绝一切用户操作,直至响应到达:
代码语言:javascript复制var req = new XMLHttpRequest();
req.open("GET", url, true); //true 表示异步,false 表示同步
req.onreadystatechange = callback;
req.send();
JavaScript 的一个悲剧
在 JavaScript 中,没有一个方法可以让主线程休息一段时间(Java 中有 sleep 和 wait),也就是说,如果我想在某一个执行逻辑中,休息一会、等待一会,这样的实现都会变得很困难(Jscex 就是用来解决这样的问题的)。这似乎是 JavaScript 的一个天生的重大缺陷。
Jscex 带来的最大好处,就在于可以用同步的思维和编码,来解决异步的问题:
代码语言:javascript复制var moveAsync = eval(Jscex.compile("$async", function(e, startPos, endPos, duration) {
for (var t = 0; t < duration; t = 50) {
e.style.left = startPos.x (endPos.x - startPos.x) * (t / duration);
e.style.top = startPos.y (endPos.y - startPos.y) * (t / duration);
$await(Jscex.Async.sleep(50));
}
e.style.left = endPos.x;
e.style.top = endPos.y;
}));
Barrier 模式
Barrier 是一道篱笆,所有的不同异步线程,都先先后后运行完毕以后(都撞到了篱笆上),再汇成一束主流程继续往后走:
JDK 的 CyclicBarrier 类就是用来实现这个模式的(A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.):
代码语言:javascript复制class Solver {
final int N;
final float[][] data;
final CyclicBarrier barrier;
class Worker implements Runnable {
int myRow;
Worker(int row) {
myRow = row;
}
public void run() {
while (!done()) {
processRow(myRow); //执行某一行的逻辑
try {
barrier.await(); //该行逻辑执行完毕后,告知一下
} catch (InterruptedException ex) {
return;
} catch (BrokenBarrierException ex) {
return;
}
}
}
}
public Solver(float[][] matrix) {
data = matrix;
N = matrix.length;
barrier = new CyclicBarrier(N,
new Runnable() {
public void run() {
mergeRows(...); //在每一行都处理完毕以后,执行一个 merge 操作
}
});
for (int i = 0; i < N; i)
new Thread(new Worker(i)).start();
waitUntilDone();
}
}
而在 JavaScript 中,也可以实现类似的效果:
代码语言:javascript复制var count = 3;
for(var i=0; i<=count; i ){
setTimeout(function(){
doXXX(); // 执行任务
count --; //每个子任务执行完毕后都标记一下
if(!count)
doFinalXXX(); //Barrier 的汇总任务
},0);
}
如果有了 Jscex,实现可以更简洁:
代码语言:javascript复制function (taskA, taskB, taskC) {
$await(Jscex.Async.parallel(taskA, taskB)); //先并行执行任务 A、B
$await(taskC); //在 A、B 都完成后再执行 C
}
Future 和 Promise
Future、Promise 是用于并发编程的一种同步构造。它们表示一个对象,这个对象用来作为一次计算的结果的代理,而该结果被初始化为未知,因为这个对象产生时计算还没有结束(或还没有开始)。
Java 中有 Future 可以帮助实现:
代码语言:javascript复制ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<Object> task = new Callable<Object>() {
public Object call() throws Exception {
doXXX();
return result;
}
};
Future<Object> future = executor.submit(task);
boolean isCancelled = future.isCancelled(); //查询状态,调用 cancel 方法可以取消任务
boolean isDone = future.isDone(); //查询状态
Object res = future.get(); // 等待至完成
JavaScript 可以实现成类似这样子:
代码语言:javascript复制Promise.when(promise1, promise2).then(function (data1, data2) {...});
具体请参见这里。
文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》
×Scan to share with WeChat