javascript是一个单线程执行程序,即它只有一个主线程,所有程序都是一行一行“排队”执行的。在这种情况下,可能会出现一些问题,比如SetTimeout和ajax等待执行时间过长,会阻塞后续代码的执行,使得整个程序的执行时间非常长,所以为了处理遇到这样的问题,javascript代码执行时,有几个“通道”。第一个是调用堆栈,它执行需要很短时间的操作。耗时长的操作优先放入任务队列。任务队列分为宏任务和微任务。微任务promise.then、aysnc、await的操作放在队列中。SetTimeout、ajax和onClick事件放在宏任务队列中。调用栈任务执行完成后,轮询微任务队列。微任务队列中的任务执行完毕。然后执行宏任务。这里提到栈和队列。下面简单说说这两种数据结构。栈是后进先出的结构,只能从尾进入,从尾删除。以生活中的场景为例,就像自助餐的第一个盘子在最下面,最后一个盘子在最上面。您需要一个接一个地拆下顶板以获得底板。队列是一个先进先出的结构,从尾部进入,从头部删除,就像我们排队买东西的时候,先去的同学可以先买。回到事件循环机制(eventloop),将不阻塞主进程的程序放入调用栈,压入栈底,执行完就会弹出。如果是函数,则弹出函数中的所有内容。阻塞主进程的程序被放入任务队列,需要“排队”才能顺序执行。首先我们举一个简单的例子来判断以下程序的执行顺序newPromise(resolve=>{console.log('promise');resolve(5);}).then(value=>{console.log('thencallback',value)})functionfunc1(){console.log('func1');}setTimeout(()=>{console.log('setTimeout');});func1();创建一个promise的实例是当一个异步任务启动时,传入的回调函数,也叫执行器函数,会立即执行,所以输入一个promise,使用resolve返回一个成功的执行结果,然后在then中执行函数会被压入微任务队列,等待调用栈依次执行。向下执行发现定义了一个func1函数。如果此时函数没有被调用,则不会被压入调用栈执行。程序继续往下,发现调用了setTimeout函数,将打印出来的setTimeout压入宏任务队列,然后执行调用函数func1,将func1压入调用栈,执行func1函数,此时输出fun1。调用栈中的内容全部执行完毕,开始轮询microtask队列,进入thencallback5,最后执行macrotask队列,进入setTimeout我们来看一个复杂的例子setTimeout(function(){console.log("set1");newPromise(function(resolve){resolve();}).then(function(){newPromise(function(resolve){resolve();}).then(function(){console.log("then4");});console.log("then2");});});newPromise(function(resolve){console.log("pr1");resolve();}).then(function(){console.log("then1");});setTimeout(function(){console.log("set2");});console.log(2);queueMicrotask(()=>{console.log("queueMicrotask1")});newPromise(function(resolve){resolve();}).then(function(){console.log("then3");});setTimeout("set1")执行的回调函数是直接放入宏任务队列等待,Promise的Excutor函数会立即执行,先输入pr1,Promise.then函数("then1")会被放入在微任务队列中等待,下面setTimeout执行的回调函数(“set2”)也会被执行放到宏任务队列中,后面(“set1”),然后在调用栈中输出2,queueMicrotask的意思启动一个微任务,与Promise.then函数的效果一致,将("queueMicrotask1")放入微任务队列中,然后再往下执行,立即执行newPromise的executor函数,并且then函数(“then3”)被放置在微任务队列中等待。这时候调用栈pr1已经依次输入,执行完2调用栈中的程序,在microtask队列中执行microtask,依次输出then1,queueMicrotask1,then3。此时microtask队列中的任务也执行完毕,来到macrotask队列中,输出set1,执行Promise的Excutor函数,resolve会返回执行成功的结果,then函数("then2")放入microtask中,一旦microtask队列中有任务,后面就不会执行macrotask了,所以此时macrotask队列中的另一个setTimeout函数(“set2”)不会执行,会在microtask队列中执行(“然后2”)。输出then2,然后执行一个promise函数,("then4")被放入microtask队列,输出then4。microtask队列也执行了,这时候,来到macrotask队列,执行set2。所以最后输出的结果是:pr12then1queueMicrotask1then3set1then2then4set2简图如下最后一题,加async,await先得出结论,async定义的函数在调用栈中执行,await把异步程序变成同步的,所以执行await之后程序需要等到await定义的函数执行完毕,需要在微任务队列中等待asyncfunctionasync1(){console.log('async1start')awaitasync2();console.log('async1end')}asyncfunctionasync2(){console.log('async2')}console.log('scriptstart')setTimeout(function(){console.log('setTimeout')},0)async1();newPromise(function(resolve){console.log('promise1')resolve();}).then(function(){console.log('promise2')})console.log('scriptend')函数只有在被调用时才会被压入调用栈,所以先执行console.log,即输出scriptstart,然后宏中放入setTimeout函数("setTimeout")任务队列等待,调用async1函数,输出async1start,执行async2函数。输出async2,("async1end")放入microtask队列等待,继续向下执行Promise函数,输出promise1,then函数中的("promise2")放入microtask队列等待,输出脚本结束。调用栈上的程序都执行完了。此时开始执行microtask队列中的程序,依次输出async1end和promise2。microtask队列中的程序也已经执行完毕,开始执行macrotask中的程序,并输出setTimeout。输出序列是scriptstartasync1startasync2promise1scriptendasync1endpromise2setTimeout。简图如下,判断执行顺序。可以记住以下几点:1、promise中的回调函数是立即执行的,then中的回调函数会被压入微任务队列,等待调用栈中的所有任务执行完2、async函数中的内容放入调用栈执行,下一行await的内容放入microtask执行。3.调用栈执行完成后,会不断轮询微任务队列,即使宏任务被压入队列,微任务也会先执行
