JavaScript 是单线程语言,也就是说同一时间只能做一件事;这是 JS 的设计所致:JavaScript 最初是为了处理页面中用户的交互,以及操作 DOM 元素
例如:操作页面上的某个 DOM 元素,现在有2 个线程,proOne 用于执行删除命令、preTwo 用于执行修改命令;如果 JS 是多线程的这两个线程就会同时对同一个 DOM 进行操作,proOne 删除了当前 DOM ,而preTwo 修改了该 DOM ,这两个线程就会产生矛盾。
单线程带来的问题:
单线程就意味着,所有任务需要排队执行;前一个任务执行结束,才会执行后一个任务;如果前一个任务耗时很长,后一个任务就不得不一直进行等待
这样就会导致:如果 JS 执行的时间过长,就会造成页面的渲染不连贯,导致页面渲染加载时有阻塞的感觉,用户体验较差。
同步任务与异步任务
在 HTML5 中提出了 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制;于是JS 中就出现了同步任务和异步任务。
同步任务
-
同步任务是在主线程上排队执行的任务,只有前一个任务执行结束,才能执行下一个任务。程序的执行顺序与任务的排列顺序是一致的、同步的。
-
同步任务都是在主线程上执行,形成一个执行栈。
比如烧水、炒菜做饭:首先要烧水,烧完后去洗菜,洗完后要切菜,切完后再炒菜,炒完后才能吃,这就是同步任务。
异步任务
-
异步任务不进入主线程,而是进入 “任务队列” ,只有 “任务队列” 通知主线程某个异步任务可以执行了,该任务才会进入主线程执行。
-
异步任务主要是通过回调函数来实现的;异步任务相关的回调函数会被添加到任务队列中
在做一件事时比较花费时间,在做这件事的同时,可以去处理其他事情,比如做饭的异步做法:烧一壶水需要花费 15 分钟,利用这 15 分钟就可以去洗菜,切菜,炒菜。
异步任务主要有以下三种类型:
- 普通事件,例如: click,change,resize 等
- 资源加载事件,例如:load,error 等
- 定时器,例如:setInterval(),setTimeout()
执行机制
先执行执行栈中的同步任务,异步任务会被放入任务队列中进入等待状态;当执行栈中的任务执行完后,会顺次读取任务队列中的异步任务,任务队列中的异步任务会结束等待状态进入执行栈开始执行。