js执行环境是‘单线程’
什么是单线程(single thread)?
指一次只能完成一件任务。
如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推
粗暴理解:【某工厂只有一条生产流水线,做啥都的一个一个排着弄】
好处:实现起来比较简单,执行环境相对单纯;
坏处:只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。
常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。
为了解决单线程这问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)
同步模式:
1)前一个任务完成后,执行下一个任务。
2)程序的执行顺序与任务的排列顺序是一致的、同步的;
粗暴理解:【同一时间之内干一件事】
异步模式:
1)前一个任务结束后,不是执行后一个任务,而是执行回调函数,
后一个任务不等前一个任务结束就执行
2)程序的执行顺序与任务的排列顺序是不一致的、异步的
粗暴理解:【同一时间,因为某事执行需要时间,就空出来先干别的事】
简言之:同步会堵塞代码执行,而异步不会。
微任务和宏任务概念
1. 宏任务:当前调用栈中执行的代码成为宏任务。
2.微任务: 当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务,可以理解为回调事件。
3. 宏任务中的事件放在callback queue中,由事件触发线程维护;微任务的事件放在微任务队列中,由js引擎线程维护。
微任务和宏任务运行机制
1. 在执行栈中执行一个宏任务。
2. 执行过程中遇到微任务,将微任务添加到微任务队列中。
3. 当前宏任务执行完毕,立即执行微任务队列中的任务。
4. 当前微任务队列中的任务执行完毕,检查渲染,GUI线程接管渲染。
5. 渲染完毕后,js线程接管,开启下一次事件循环,执行下一次宏任务(事件队列中取)。
微任务:process.nextTick、MutationObserver、Promise.then、process.nextTick、 catch finally
宏任务:I/O、setTimeout、setInterval、setImmediate、requestAnimationFrame 、new Promise、async
process.nextTick() > Promise.then() > setTimeout > setImmediate;
运行方式:先同步任务 再异步任务!
先宏任务 再微任务!
【同步任务时,先执行同步宏任务再执行同步微任务,同步操作完成后,按照先后顺序执行异步操作,先执行异步宏任务再执行异步微任务,若再遇到异步或微任务就抛出就抛出】
注:Promise本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行,
async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回(如返回awaite紧跟的函数),然后再阻塞
例子1:
1 setTimeout(function() {
2 console.log('setTimeout');
3 })
4
5 new Promise(function(resolve) {
6 console.log('promise');
7 //resolve();
8 }).then(function() {
9 console.log('then');
10 })
11
12 console.log('console');
结果:promise console setTimeout
说明:
1 整段代码作为宏任务进入主线程
2 遇到settimeout,将其回调函数注册后分发到宏任务Event Queue。
3 遇到了Promise,new Promise立即执行,then函数分发到微任务Event Queue
4 遇到console.log(),立即执行
5 第一个宏任务执行结束,看看有什么微任务,发现有then,执行
6 第二轮循环,发现宏任务settimeout的回调函数,执行。
7 结束。
例子2:
console.log("1"); //第一轮主线程【1】 setTimeout(function() { //碰到set异步,丢入宏任务队列【set1】:我将它命名为set1 console.log("2"); //第二轮宏任务执行,输出【2】 process.nextTick(function() { //第二轮宏任务执行,碰到process,丢入微任务队列,【3】 console.log("3"); }); new Promise(function(resolve) { //第二轮宏任务执行,输出【2,4】 console.log("4"); resolve(); }).then(function() { console.log("5"); //第二轮宏任务执行,碰到then丢入微任务队列,【3,5】 }); }); process.nextTick(function() { //碰到process,丢入微任务队列【6】 console.log("6"); //第一轮微任务执行 }); new Promise(function(resolve) { console.log("7"); //new的同时执行代码,第一轮主线程此时输出【1,7】 resolve(); }).then(function() { console.log("8"); //第一轮主线程中promise的then丢入微任务队列,此时微任务队列为【6,8】。当第一轮微任务执行,顺序输出【6,8】 }); setTimeout(function() { //碰到set异步丢入宏任务队列,此时宏任务队列【set1.set2】:我将它命名为set2 console.log("9"); //第三轮宏任务执行,输出【9】 process.nextTick(function() { //第三轮宏中执行过程中添加到微任务【10】 console.log("10"); }); new Promise(function(resolve) { console.log("11"); //第三轮宏任务执行,宏任务累计输出【9,11】 resolve(); }).then(function() { console.log("12"); //第三轮宏中执行过程中添加到微任务【10,12】 }); });
结果:1,7,8,2,4,5,6,9,11,12,3,10
例子3:例子2稍微修改
console.log("1"); setTimeout(function() { console.log("2"); process.nextTick(function() { console.log("3"); }); new Promise(function(resolve) { console.log("4"); }).then(function() { console.log("5"); }); }); process.nextTick(function() { console.log("6"); }); new Promise(function(resolve) { console.log("7"); resolve(); }).then(function() { console.log("8"); }); setTimeout(function() { console.log("9"); process.nextTick(function() { console.log("10"); }); new Promise(function(resolve) { console.log("11"); }).then(function() { console.log("12"); }); });
结果 :1,7,8,2,4,6,9,11,3,10
关于 async awite 推荐别人写的
https://blog.csdn.net/VhWfR2u02Q/article/details/84948640
同步场景
如:alert() prompt()
**前端使用异步场景
在可能发生等待的情况
1).定时任务:setTimeout setInterval
2).网络请求:ajax请求,动态<img>加载
3).事件绑定:addEventListener
例子1:
1 console.log(100);
2 setTimeout(function(){
3 console.log(200);
4 },1000);
5 console.log(300);
结果:100 300 200
例子2:
1 console.log('img开始');
2 var img = document.createElement('img');
3 img.onload = function(){
4 console.log('loaded');
5 }
6 img.src="/xxx.png";
7 console.log('img结束');
结果:img开始 img结束 loaded
例子3:
1 console.log('事件开始!');
2 var btn1 = document.getElementById('btn1');
3 btn1.addEventListener('click',function(){
4 console.log('你点击我了');//点击了才会显示
5 })
6 console.log('事件结束!!');
结果:事件开始! 事件结束! 你点击我了
例子4:
1 console.log(1);
2 setTimeout(function(){
3 console.log(2);
4 },1500);
5 console.log(3);
6 setTimeout(function(){
7 console.log(4);
8 },300);
9 console.log(5);
结果: 1 3 5 4 2 //根据封禁时间解封