在上一篇彻底弄清Javascirpt中的同步和异步一问当中,介绍了Javascirp代码执行的概念,同步和异步的原理。
学习过Javascript语言的同学都知道,从Javascript诞生之日起,就是一门单线程、非阻塞的脚本语言。Javascript代码在执行的时候,都有一个主线程来处理所有任务,非阻塞就是靠异步编程,即事件循环(Event loop)。
本文将向大家讲述,在js当中如何实现异步编程的。
在本文之前先了解一下队列的概念
队列 (queue)队列的特点是是"FIFO,即先进先出(First in, first out)" 。 数据存取时"从队尾插入,从队头取出"。
"与栈的区别:栈的存入取出都在顶部一个出入口,而队列分两个,一个出口,一个入口"。
JS为何需要异步?
for (var i=0;i 微任务 -> 宏任务 -> 微任务 ···
这个例子看懂基本js执行机制就理解了
console.log(1);
setTimeout(function() {
console.log(2);
})
var promise = new Promise(function(resolve, reject) {
console.log(3);
resolve();
})
promise.then(function() {
console.log(4);
})
console.log(5);
示例中,setTimeout 和 Promise被称为任务源,来自不同的任务源注册的回调函数会被放入到不同的任务队列中。
有了宏任务和微任务的概念后,那 JS 的执行顺序是怎样的?是宏任务先还是微任务先?
第一次事件循环中,JavaScript 引擎会把整个 script 代码当成一个宏任务执行,执行完成之后,再检测本次循环中是否寻在微任务,存在的话就依次从微任务的任务队列中读取执行完所有的微任务,再读取宏任务的任务队列中的任务执行,再执行所有的微任务,如此循环。JS 的执行顺序就是每次事件循环中的宏任务-微任务。
- 上面的示例中,第一次事件循环,整段代码作为宏任务进入主线程执行首先输出1,遇到
Promise
,new Promise
直接执行,输出3,,然后是5。 - 遇到 Promise,将 then 函数放入到微任务的微任务Event Queue中。
- 遇到了 setTimeout ,就会等到过了指定的时间后将回调函数放入到宏任务的任务队列中。
- 整个事件循环完成之后,会去检测微任务的任务队列中是否存在任务,存在就执行。
- 第一次的循环结果打印为: 1,3,5,4。
- 接着再到宏任务的任务队列中按顺序取出一个宏任务到栈中让主线程执行,那么在这次循环中的宏任务就是 setTimeout 注册的回调函数,执行完这个回调函数,发现在这次循环中并不存在微任务,就准备进行下一次事件循环。
- 检测到宏任务队列中已经没有了要执行的任务,那么就结束事件循环。
- 最终的结果就是 1,3,5,4,2。