sync-and-async

同步?异步?

虽然是个菜,但是我还是有话要说。在学JS,等下就分析几个浏览器中JS代码片段。

同步,异步不知道谁翻译的。我书读的不多。可是觉得滥用了。这两个词,哎。我还是没有想的特别清晰。试着谈谈吧。

还得从其他地方说去,long long ago…

  1. 开始你有两只手,你左手码字,右手撸管。(手就是双核,码字,撸管就是进程啥的,脑子来调度。此操作可以是并行的,互不干扰,不行就让别人帮你。当然你也可以并发,去刷牙…)
  2. 后来你觉得没有节奏感,应该码一字,撸一下。此时还是并行,不过同步了一下。(什么同步?谁和谁?)
  3. 再后来你不小心被小郭妹子断了右手。
  4. 你在想该怎么办?
  • 你左手码完字,然后左手撸啊撸。(单任务)
  • 你左手码一字,左手又去撸一下。(并发了。不过来回切换上下文,也累啊。)

说了半天,什么是同步?异步?第二步提到的同步是啥?因为在第二步中,我们为了追求节奏感,可以认为这两个进程或者操作变成了一个大任务。而同步异步应该说的就是一个大任务中的各个小任务之间的执行顺序。我在人民广场吃炸鸡,你在网吧打游戏,这同不同步,异不异步有何用?除非弄成一个任务的两部分。

这里我们要提到层次结构。因为任务是可以细化的。

一个进程的生命周期中,每个线程一定是按照一定的次序执行的。不管是挨个执行指令,还是分支,循环,跳转……都是按次序的。虽然线程与线程之间看起来没了先后次序,那每个线程内部还是按次序执行。对这些操作系统理论,不清楚。但我坚信,局部是有次序的,它们一定会遵循一定的原则。一堆进程,看起来,乱乱的,但是每个进程内部都是有严格次序的,不会跑过去执行另一个进程空间的指令。就进程内部来说,一堆线程,乱乱的,可是每个线程也是有自己的次序。次序永存。

直接上码。无码最可耻。

1
2
3
4
5
6
7
function job1(){return "job1";}
function job2(){return "job2";}
function job3(){return "job3";}

job1();//1
job2();//2
job3();//3

这段代码的执行,有次序,1=>2=>3。

1
2
3
4
5
6
7
function job1(){return "job1";}
function job2(){return "job2";}
function job3(){return "job3";}

job1();//1
setTimeout(job2, 24*60*60*1000);//2
job3();//3

这段代码的执行,有次序,1=>2=>3。 job1执行;然后setTimeout执行,这里发生了一件事,它郑重的把job2和86400000交给了Timer线程,然后立马返回一个timeoutID(定时器编号,可使用clearTimeout清除),不过我们没保存这个值;然后job3执行。对吗?线程内还是按次序执行,一步一步来嘛(同步这个词实在让人困惑)。job1=>setTimeout=>job3,不费吹灰之力。大概在第二天的此刻却发生了一个可怕的事情,job2冒了出来(异步这个词也实在让人困惑)。在我看来他们完全是按照次序,即使你把job2放到事件队列,那还是按次序来,job2它自己内部的代码也是按次序在主线程执行。a=>b=>c是次序, b=>a=>c也是次序。job1,job2,job3每个里面都可以再加任务。

  1. 就算你把第二句换成 ajax(some-url, job2),还是job1=>ajax=>job3这个次序,(ajax把some-url, job2交给其他线程,立马返回。那个线程干完活,就把job2加到队列)不知啥时候job2会给你来一下。
  2. 如果ajax不是立马返回,(浏览器应该是废弃这种用法了),次序就是job1=>ajax-job2=>job3。(ajax-job2, ajax执行,调用job2,ajax返回)

如果不是立马返回,还有啥意思,难道要setTimeout(job2, 24*60*60*1000);一直等?不管在现实生活中还是在计算机系统中,都是不合理的!就是要把job2扔到队列中。他们把这种叫异步编程。如今,你可以想办法把它扔到task queue,或者microtask queue。但是话又说回来,当你明白了次序,你也会明白,啥都往队列中塞,也是不好的做法;举个栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
function job1() {return "job1";}
function job2() {
let i=0;
while(i<Number.MAX_SAFE_INTEGER){
i++;
}
return "job2";
}
function job3() {return "job3";}

job1();//1
setTimeout(job2, 1000);//2
job3();//3

你把job2扔到队列中也是没用的。到头来,它还是会回到主线程执行,说不定卡死。这种应该新开个Woker线程。

瞎扯事件

什么是事件。你对着浏览器大喊,“你个2B”,浏览器显示,“滚蛋”。这就是声音事件和浏览器对事件的处理。可惜的是,浏览器不是你实现的,要是你实现的,你想加什么事件都行,你还可以完全不处理鼠标事件。不过,估计也没有什么人会用你的浏览器了。

浏览器中有很多预定义的事件。你点一下鼠标,按一下键盘,都会出事。浏览器这个应用程序也真是复杂啊,操作系统GUI有一套消息机制,浏览器拿到这些消息,又在内部实现了自己的事件机制。一般的应用程序,处理下操作系统的消息就不错了,像win32中的 WM_QUIT……。Windows中的消息,有的消息是不进队列的,有的消息有时候不进有时候进。那看看浏览器。

1
2
3
4
5
6
7
8
9
function _2b(z){console.log('2B');}
window.addEventListener('click', _2b)
/*
some jobs
*/
document.body.click();
/*
some tasks
*/

一般用户都是用鼠标点击页面,_2b入队列。 代码中click函数触发,直接调用 _2b,不入队列。

我现在也没想好下面说啥。但我不怎么喜欢在这里用同步、异步。就是Script执行完,掏空microtask,然后UI善后;再拿一个task,完了掏空microtask,UI善后;……

谁和谁同步,谁和谁异步?晕。把job2放到队列叫异步?那拿到job2执行的时候,不就是同步了?同步的任务,里面没有异步代码?异步的任务,里面没有一句同步代码?两个语句什么关系?两个函数什么关系?两个任务什么关系?两个模块什么关系?你把馒头吃到嘴里,再到胃里。若说这两个是同步任务,那吃到嘴里细分,切牙咬和磨牙磨这小任务是什么关系?我觉得不需要这些伪概念。我觉得JS运行时这个层面,没啥同步、异步,只有Event Loop。所有的任务都遵守着看不见的秩序。

未完待续