javascript的同步和异步
由于javascript是同步代码,是按照代码顺序从上而下执行。但是由于一切特殊的方法,没办法用同步的机制。比如dom事件,setTimeOut,ajax这些。如果采用同步的方式,则会阻碍后面代码的执行。所以js中引入了异步的概念。
js中的异步,由于js本身是单线程,不支持多线程。但是运行js的环境(浏览器)是可以多线程运行的(就像后端php也是单线程的,但是nginx是多线程的。可以一次性处理多个php请求是一个道理)。浏览器会分配3个线程,一个是处理前端的页面渲染(html+css),一个是处理js的代码,一个是处理js的异步回调。
那js的异步代码执行的先后顺序是怎样的呢?他遵循一个什么样的机制?
这里就要讲到js的事件循环。简单的说,就浏览器会先执行所有的同步代码。然后把异步的代码放入任务队列中,等同步代码执行栈中的代码全部执行完以后,再按照任务队列中先进先出的顺序从任务队列中把异步代码拿回来执行。
这里涉及到两个概念。宏任务和微观任务。
宏任务包括setTimeout、setInterval、 setImmediate、script(整体代码)——>第一个宏任务、I/O 操作等
微任务包括,process.nextTick、Promise、MutationObserver等,主要是promise
-
初始状态下,调用栈空。微任务队列空,宏任务队列里有且只有一个 script 脚本(整体代码)。这时首先执行并出队的就是 整体代码
-
整体代码作为宏任务进入调用栈,进行同步任务和异步任务的区分
-
同步任务直接执行并且在执行完之后出栈,异步任务进行微任务与宏任务的划分,分别被推入进入微任务队列和宏任务队列
-
等同步任务执行完了(调用栈为空)以后,再处理微任务队列,将微任务队列压入调用栈
-
当调用栈中的微任务队列被处理完了(调用栈为空)之后,再将宏任务队列压入调用栈,直至调用栈再一次为空,一次轮回结束
整体的运行流程可以查看下图,红色箭头为主要的执行流程,整体代码(宏任务) => 同步任务 => 微任务队
列 => 宏任务队列
注:promise有些特殊。promise内部的代码属于同步代码,在执行同步代码的时候会先执行。then里面的回调函数属于微任务代码
例:
var p = new Promise(function(resolve,reject){
resolve();
console.log("同步代码执行");
}).then((ret) =>{
console.log("微任务代码执行");
})