前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >同步、异步转化和任务执行

同步、异步转化和任务执行

作者头像
四火
发布2022-07-15 19:53:15
5870
发布2022-07-15 19:53:15
举报
文章被收录于专栏:四火的唠叨四火的唠叨

正如动静是相对的概念,有了它们,世界才充满盎然生气;变和不变也是哲学上的对立统一,在代码的世界里也一样;同步异步呢?

首先,来粗略地看看同步和异步各自有些什么好处

同步的好处:

  • 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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档