js执行环境是‘单线程’
什么是单线程(single thread)?
指一次只能完成一件任务。
如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推
好处:实现起来比较简单,执行环境相对单纯;
坏处:只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。
常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。
为了解决单线程这问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)
同步模式:
1)前一个任务完成后,执行下一个任务。
2)程序的执行顺序与任务的排列顺序是一致的、同步的;
异步模式:
1)前一个任务结束后,不是执行后一个任务,而是执行回调函数,
后一个任务不等前一个任务结束就执行
2)程序的执行顺序与任务的排列顺序是不一致的、异步的
简言之:同步会堵塞代码执行,而异步不会。
js异步运行机制之微任务和宏任务
异步执行的运行机制如下(同步任务也如此,因为它可以被视为没有异步任务的异步执行):
1.所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)
2.主线程之外,还存在一个“任务队列”,只要异步任务有了运行结果,就在“任务队列”之中放置一个事件
3.一旦“执行栈”中的所有同步任务执行完毕,系统就会读取“任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待,进入执行栈,开始执行
4.主线程不断重复第3步
微任务和宏任务
macro-task(宏任务):包括整体代码script,setTimeout,setInterval
micro-task(微任务):Promise,process.nextTick
任务队列:宏任务队列 、微任务队列
不同类型的任务会进入不同的任务队列(Event Queue)
事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。
然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。
例子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:
1 console.log('1'); 2 3 setTimeout(function() { 4 console.log('2'); 5 process.nextTick(function() { 6 console.log('3'); 7 }) 8 new Promise(function(resolve) { 9 console.log('4'); 10 //resolve(); 11 }).then(function() { 12 console.log('5') 13 }) 14 }) 15 process.nextTick(function() { 16 console.log('6'); 17 }) 18 new Promise(function(resolve) { 19 console.log('7'); 20 //resolve(); 21 }).then(function() { 22 console.log('8') 23 }) 24 25 setTimeout(function() { 26 console.log('9'); 27 process.nextTick(function() { 28 console.log('10'); 29 }) 30 new Promise(function(resolve) { 31 console.log('11'); 32 //resolve(); 33 }).then(function() { 34 console.log('12') 35 }) 36 })
结果:1,6,7,2,3,4,9,10,11
例子3:例子2升级版
1 console.log('1'); 2 3 setTimeout(function() { 4 console.log('2'); 5 process.nextTick(function() { 6 console.log('3'); 7 }) 8 new Promise(function(resolve) { 9 console.log('4'); 10 resolve(); 11 }).then(function() { 12 console.log('5') 13 }) 14 }) 15 process.nextTick(function() { 16 console.log('6'); 17 }) 18 new Promise(function(resolve) { 19 console.log('7'); 20 resolve(); 21 }).then(function() { 22 console.log('8') 23 }) 24 25 setTimeout(function() { 26 console.log('9'); 27 new Promise(function(resolve) { 28 console.log('11'); 29 resolve(); 30 }).then(function() { 31 console.log('12') 32 }) 33 })
结果 :1,6,7,8, 2,3,4,5, 9,11,12
同步场景
如: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 //根据封禁时间解封