由定时器引发的关于JS执行机制的详细了解
问题:若有多个计时器则与设置时间执行不同,为什么?
初见这个问题,自然想到JS的执行机制以及同步与异步
,深入了解之后,收获颇丰。下面简单总结一下。
1.JS是单线程非阻塞语言。
所谓线程,指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。而进程是线程的容器。程序是指令、数据及其组织形式的描述,进程则是是程序的实体。
阻塞和非阻塞则关注的是程序在等待调用结果(消息,返回值)时的状态.
阻塞调用: 指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回
非阻塞调用: 指在不能立刻得到结果之前,该调用不会阻塞当前线程
2.同步和异步
同步操作
是放在执行栈中执行,栈遵循先进后出。下面是一个例子:
let A = () => {
B()
console.log(1);
};
let B = () => {
C()
console.log(2);
};
let C = () => {
console.log(3);
};
A();//3 2 1
异步操作
放在任务队列中。队列遵循先进先出。
当一个任务被执行,js会判断是否为同步任务,如果是同步,压入主线程立即执行;但如果是异步任务,移步异步处理模块(Task Table),当异步任务有了结果,就将异步任务的回调函数注入到任务队列中等待。
当主线程的同步任务执行完毕执行栈为空,js引擎就会读取任务队列中的第一个任务加入到执行栈执行,当此任务完成,继续重复此类操作,这也就是事件循环了
3.定时器
一个简单的定时器:
setTimeout(() => console.log('First'), 3000);
站在宏观思想上理解,这行代码的意思是这个定时器将在三秒后触发,但站在微观的角度上,3000ms并不代表执行时间,而是将回调函数加入任务队列的时间,这也是为何存在定时器执行与所设置等待时间不符的问题所在。
一个例子:
setTimeout(() => console.log('First'), 3000);
setTimeout(() => console.log('Sec'), 3000);
上述代码真正执行是等待三秒后几乎无间隔的同时打印2个结果。
4.关于异步中的宏任务与微任务
宏任务: setTimeout、setInterval、I/O、事件、postMessage、 MessageChannel、setImmediate (Node.js)
微任务: Promise,process.nextTick,MutaionObserver
微任务优先级总是高于宏任务。
附图:
参考: