-
js是一门单线程的语言,不像Java,类继承Thread再来个thread.start就可以开辟一个线程。js就像一条单一流水线,同一个时间只能做一个任务。
-
同步和异步的差别在于这条流水线上各个流程的执行顺序不同。
-
最基础的异步是setTimeout和setInterval函数,很常见,但很少有人知道这就是异步。
-
<script type="text/javascript"> setTimeout(function(){ console.log("1") },0); console.log("2"); </script>
输出顺序是
任务队列,依次存放setTimeout中的function,在执行程序的时候,浏览器会默认setTimeout以及ajax请求这一类的方法都是耗时程序(尽管可能不耗时),将其加入一个队列中,该队列是一个存储耗时程序的队列,在所有不耗时程序执行过后,再来依次执行该队列中的程序。尽管setTimeout的time延迟时间为0,其中的function也会被放入一个队列中,等待下一个机会执行,当前不需要加入队列中的程序必须在队列的程序完成之前完成。
-
js是单线程,意味着所有的任务需要排队,等一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。于是有了任务队列
如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。于是JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,**挂起处于等待中的任务,先运行排在后面的任务。**等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。
-
所有任务可以分为两种
-
同步任务(synchronous),指的是在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务
-
如果在函数返回结果的时候,调用者能够拿到预期的结果(就是函数计算的结果),那么这个函数就是同步的。即使比较慢也会等他执行完。
console.log('hello');
-
-
异步任务(asynchronous),指的是不进入主线程,而进入任务队列(task queue)的任务,只有等主线程任务执行完毕,任务队列开始通知主线程,请求执行任务,该任务才会进入主线程执行
- 在函数返回的时候,调用者还不能立即得到预期结果,而是将通过一定手段得到(例如回调函数),例如ajax操作。调用者不必主动等待,当被调用者得到结果之后会通过回调函数主动通知调用者。
-
-
异步运行机制
- 所有的同步任务都在主线程上执行,形成一个执行栈(execution context stack)
- 主线程之外,还存在一个任务队列task queue。只要异步任务有了运行结果,就在任务队列之中放置一个事件。
- 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面有哪些事件,对应的异步任务结束等待状态进入执行栈开始执行。
- 主线程不断重复上面的第三步。
-
IO设备完成一项任务,就在任务队列中添加一个事件,表示相关的异步任务可以进入执行栈了,主线程读取任务队列,就是读取里面有哪些事件。
-
任务队列中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等),比如 $(selectot).click(function),这些都是相对耗时的操作。只要指定过这些事件的回调函数,这些事件发生时就会进入任务队列,等待主线程读取。
-
回调函数,就是那些会被主线程挂起来的代码,$(selectot).click(function)中的function就是一个回调函数。异步任务必须指定回调函数(callback),当主线程开始执行异步任务,就是执行对应的回调函数。例如ajax的success,complete,error也都指定了各自的回调函数,这些函数就会加入任务队列中等待执行。
-
js是单线程是因为浏览器在运行时只开一个js解释器,有两个线程操作DOM浏览器就晕了。浏览器不是单线程的,一些IO操作,定时器的计时和事件监听是由其他线程完成的。