js的线程问题
首先需要明确 JS 是单线程的,这是 Javascript 语言的一大特点。单线程即同一时间程序只能处理一件事。这是因为 javascript 这门脚本语言诞生的使命所致——javascript 是为处理页面中用户的交互,以及操作 DOM 而诞生的。比如我们对某个 DOM 元素进行添加和删除的操作不能同时进行。单线程就意味着所有的任务需要排队,前一个任务结束后才能执行下一个任务。这样出现的问题是:如果 JS 执行时间过长会导致页面渲染不连贯,导致页面加载阻塞的感觉。
抛出问题
以下三个案例的结果是什么?
function test() {
console.log(3);
}
// 第一段代码
// console.log(1);
// setTimeout(test, 1000);
// console.log(2);
// 第二段代码
// console.log(1);
// setTimeout(test, 0);
// console.log(2);
// 第三段代码
console.log(1);
document.onclick = function() {
console.log('click');
}
console.log(2);
setTimeout(test, 3000)
同步和异步
为解决上述线程问题,利用多核 CPU 的计算能力,H5 提出了 Web Worker 标准,允许 javascript 脚本创建多个线程。于是,JS 中出现了同步和异步。
同步:前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。
异步:你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情。
js中的同步任务和异步任务
同步任务
JS 中的同步任务是指在主线程上排队执行的任务,只有上一个任务执行完毕才能继续执行下一个任务。这些在主线程上执行的任务形成一个执行栈。
异步任务
异步任务不进入主线程,程序在遇到异步任务时会提交给对应的异步进程处理。异步任务经异步进程处理完毕(可以执行了)后将被推入任务队列(异步队列)。JS 中异步是通过回调函数实现的。一般而言,异步任务有以下三种类型:
- 普通事件,例如 click、resize 等。这些异步任务是在触发事件时就将异步任务推入任务队列中。
- 资源加载,如 load、error 等。这些任务是在加载完毕后将异步任务推入任务队列。
- 定时器,包括 setTimeout、setInterval 等。在指定时间将异步任务推入任务队列。
js执行机制
执行机制如图所示:
- 先执行执行栈中的同步任务。
- 遇到异步任务则将其提交给对应的异步进程处理。
- 异步任务经异步进程处理完毕被推入任务队列(也称消息队列)进行排队等待执行。即此时可以进入执行栈了。
- 一旦执行栈中的所有同步任务执行完毕,系统会按照先进先出的原则读取任务队列的异步任务。被读取的异步任务结束其等待状态,进入执行栈,开始执行。
- 主线程从任务队列中读取任务的过程是不断循环的,每次执行栈被清空后,都会到任务队列中读取新的任务,如果没有任务则会等待直到新任务被推入任务队列。这种机制称为事件循环(event loop)。
注意:在先执行执行栈中的同步任务时,如果任务队列中已经存在异步任务,但由于同步任务还没有完成所以异步任务此时也不会被执行。(案例2)