最近发现挺多人在学习js的时候,比较难理解同步和异步的;特别在刚学习到定时器时,我们设置定时器延迟执行的时间,JS真的会准确按照这个时间间隔来执行吗?
下面我们就从定时器开始慢慢聊一下这个话题。
首先要明确两点:
- JS 执行机制是单线程。
- JS的Event loop是JS的执行机制,深入了解Event loop,就等于深入了解JS引擎的执行。
单线程执行带来什么问题?
在JS执行中都是单线程执行,所以代码的执行可以说是自上而下,如果前一段的代码比较耗时或者出错,就会导致下一段代码的阻塞现象,直观一点就是用户能感受到的的卡顿或者崩溃,所以在JS执行机制引出了异步执行操作。
那异步能解决什么问题,又会带来什么问题?
异步操作能够很好的解决上面单线程执行出现的卡死现象,但是也会产生问题,比如同时对一件事情操作,不知道应该先执行那件事。
那么同步中使用异步如何实现呢?
是通过的事件循环(Event loop)那先了解下Event loop吧。
先看一下,以下代码:
- console.log("1");
- setTimeout(function(){
- console.log("2");
- },0);
- console.log("3");
运行结果是:1、3、2
总所周知setTimeout里的函数不会立即执行,而是延迟一段时间,符合特定的条件才开始执行,这就是异步执行操作。
JS中所有任务可以分成两种,一种是同步任务,另一种是异步任务(如各种浏览器事件、定时器和Ajax等)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
直接结合图例来说明:
- 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
- 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
- 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
- 主线程不断重复上面的第三步。
简单总结一下: JS要做的就是执行"执行栈"里的任务,完成所有的同步任务(JS主线程)后才去读取"任务队列"中读取任务并且添加到"执行栈"中执行,这个过程不断循环。
那么看完以上的分析,回归到我们文章的第一个疑问,假设我们设置定时器的1000ms,JS真的在1000ms后执行吗??