先看一段代码html
console.log('打印'+1); setTimeout(function(){ console.log('打印'+2); }) new Promise(function(resolve,reject){ console.log('打印'+3); }).then( console.log('打印'+4));; console.log('打印'+10); new Promise(function(resolve,reject){ setTimeout(function () { console.log('打印'+5); }); }).then( console.log('打印'+6)); setTimeout(function(){ new Promise(function(resolve,reject){ console.log('打印'+7); }); })
执行结果:前端
console.log('打印'+1); setTimeout(function(){ console.log('打印'+2); }) new Promise(function(resolve){ console.log('打印'+3); resolve(); }).then(function(){ console.log(4); } ); console.log('打印'+10); new Promise(function(resolve){ setTimeout(function () { console.log('打印'+5); }); resolve(); }).then(function(){ console.log('打印'+6)}); setTimeout(function(){ new Promise(function(resolve){ console.log('打印'+7); }); }) //执行结果: //1;3;10;4;6;2;5;7
能够看出Promise比setTimeout()先执行。vim
由于Promise定义以后便会当即执行,其后的.then()是异步里面的微任务。promise
而setTimeout()是异步的宏任务。浏览器
引自https://www.cnblogs.com/woodyblog/p/6061671.html 网络
js是单线程语言,但js的宿主环境(好比浏览器,Node)是多线程的,宿主环境经过某种方式(事件驱动,下文会讲)使得js具有了异步的属性。多线程
js是单线程语言,浏览器只分配给js一个主线程,用来执行任务(函数),但一次只能执行一个任务,这些任务造成一个任务队列排队等候执行,但前端的某些任务是很是耗时的,好比网络请求,定时器和事件监听,若是让他们和别的任务同样,都老老实实的排队等待执行的话,执行效率会很是的低,甚至致使页面的假死。因此,浏览器为这些耗时任务开辟了另外的线程,主要包括http请求线程,浏览器定时触发器,浏览器事件触发线程,这些任务是异步的。异步
刚才说到浏览器为网络请求这样的异步任务单独开了一个线程,那么问题来了,这些异步任务完成后,主线程怎么知道呢?答案就是回调函数,整个程序是事件驱动的,每一个事件都会绑定相应的回调函数,举个栗子,有段代码设置了一个定时器函数
setTimeout(function(){ console.log(time is out); },1000);
执行这段代码的时候,浏览器异步执行计时操做,当1000ms到了后,会触发定时事件,这个时候,就会把回调函数放到任务队列里。整个程序就是经过这样的一个个事件驱动起来的。
因此说,js是一直是单线程的,浏览器才是实现异步的那个家伙。oop
导图要表达的内容用文字来表述的话:
(1)全部同步任务都在主线程上执行,造成一个执行栈(execution context stack)。
(2)主线程以外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的全部同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,因而结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。
主线程从"任务队列"中读取事件,这个过程是循环不断的,因此整个的这种运行机制又称为Event Loop(事件循环)。
为了更好地理解Event Loop,请看下图(转引自Philip Roberts的演讲《Help, I'm stuck in an event-loop》)。
上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各类外部API,它们在"任务队列"中加入各类事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。
异步任务有宏任务和微任务。
2.宏任务macrotask:
(事件队列中的每个事件都是一个macrotask)
优先级:主代码块 > setImmediate > MessageChannel > setTimeout / setInterval
好比:setImmediate指定的回调函数,老是排在setTimeout前面
3.微任务包括:
优先级:process.nextTick > Promise > MutationObserver
下面这个代码输出结果是什么?
主程序和和settimeout都是宏任务,两个promise是微任务
第一个宏任务(主程序)执行完,执行所有的微任务(两个promise),再执行下一个宏任务(settimeout),因此结果为:
执行结果: