JavaScript:单线程、同步执行 和 异步回调

253 阅读2分钟

在JavaScript中存在异步的概念:异步函数,用回调处理异步,用promise处理异步......

但千真万确的是,JavaScript是同步执行的、单线程的、一次只能做一件事情。

Synchronous and Asynchronous

首先看两个词,强烈建议:在观念里用它们取代“同步”和“异步”。

Synchronous:

one thing at a time

Asynchronous:

more than one thing at a thime

最初接触同步/异步概念的时候觉得非常奇怪,同步(synchronous)在中文中,是一起做,一个时间得做几个事情;异步(asynchronous)则反之。

但在这里中,它们的意思却恰好相反,Synchronous是一个时间点下只做一件事,Asychronous是一个时间点下做几件事情。

但如果看这两个单词,思路会清晰很多,syn是词根,代表“一”,跟some起源一样。

单线程的JavaScript通过回调处理处理异步

JSE(JavaScript Engine)是单线程的,但它能处理异步任务。

发生在一个浏览器中,一个时间点下发生的事情不只一个: JSE在一行一行读者JS代码;渲染引擎在绘制页面;Http模块在发送、接收数据...... 它们是在同时进行的。

浏览器在做多件事情,JSE只能做一件事情。并且,浏览器里的其他人,和JSE是有“交流”的。

他们可能会加塞给JSE一个新的任务,这就需要JSE能处理异步的能力。

JSE的处理方式是,开一个event queue。把所有来自浏览器内部,JSE外部的,需要被执行的任务,都放入event queue中。等执行栈空闲了,在从event queue里一个个读取,执行。

用这段代码来举例:

function waitThreeSeconds() {           //函数被调用三秒之后打印finished function
    var ms = 3000 + new Date().getTime();
    while (new Date() < ms){}
    console.log('finished function');
}

function clickHandler() {
    console.log('click event!');   
}

// listen for the click event
document.addEventListener('click', clickHandler);//点击屏幕后调用clickhandler,打印click event


waitThreeSeconds();
console.log('finished execution');

如果不点击屏幕,在三秒之后会先后打印:

finished function
finished execution

如果函数执行的三秒内点击屏幕:

finished function
finished execution
click event!

clickHandler被放到event queue中了,当所有写下的代码执行完,才会轮到它。这个clickHander是回调函数,当click这个异步事件发生了,执行栈空了,异步回调函数才开始被执行。