【2期】JS 执行机制+code讲解

目录

 

tips

任务

       同步任务(SyncTask)

       异步任务(AsyncTask)

       进程 && 线程

事件循环(event loop)

执行顺序

常见理解误区

         code挑战:


tips

  1. js是单线程,异步是用同步方法模拟实现的。
  2. event loop 是js的执行机制

任务

                |````宏任务:整体代码script、setTimeout、setInterval、 I/O 操作、UI 渲染等

       任务-|

                |__微任务: Promise.then、process.nextTick、catch、finally

       一个event loop有一个或者多个task队列。当用户代理安排一个任务,必须将该任务增加到相应的event loop的一个tsak队列中。每一个task都来源于指定的任务源,比如可以为鼠标、键盘事件提供一个task队列,其他事件又是一个单独的队列。可以为鼠标、键盘事件分配更多的时间,保证交互的流畅。

    同步任务(SyncTask)

       主线程来执行的时候立即就能执行的代码 

    异步任务(AsyncTask)

      说白了,异步任务就是你先去执行别的 task,等我这 xxx 完之后再往 Task Queue 里面塞一个 task 的同步任务来等待被执行

    进程 && 线程

  •  进程是操作系统分配资源的最小单位,线程是程序执行的最小单位。
  • 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
  • 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号)。
  • 调度和切换:线程上下文切换比进程上下文切换要快得多

事件循环(event loop)

       Javascript 有一个主线程 main thread,和调用栈 call-stack 也称之为执行栈。所有的任务都会放到调用栈中等待主线程来执行。

image.png

    • 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
    • 当指定的事情完成时,Event Table会将这个函数移入Event Queue,需要注意的是:当 macro-task 出队时,任务是一个一个执行的;而 micro-task 出队时,任务是一队一队执行的。
    • 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
    • 上述过程会不断重复,也就是常说的Event Loop(事件循环)。

执行顺序

        进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,当某个宏任务执行完后,会查看是否有微任务队列。如果有,先执行微任务队列中的所有任务,如果没有,会读取宏任务队列中排在最前的任务,执行宏任务的过程中,遇到微任务,依次加入微任务队列。栈空后,再次读取微任务队列里的任务,依次类推。

浏览器和 Node 环境下,microtask 任务队列的执行时机不同

  • Node 端,microtask 在事件循环的各个阶段之间执行
  • 浏览器端,microtask 在事件循环的 macrotask 执行完之后执行

常见理解误区

  1. setTimeout(fn,x): 理解为x毫秒之后就执行fn的说法是不太准备的,有时候需要等待的时间远大于x毫秒(ep: 同步线程的任务执行时间很长)。==> 真实情况是,经过x毫秒后把fn任务添加到异步的event queue中,等待主线程同步任务栈执行完毕后执行执行。
  2. setInterval(fn, x): 理解为每隔x毫秒就执行一次fn也是不准确的,理由同上。==>准确来说,每隔x毫秒,就会将fn任务添加到event queue中一次。(如果fn执行时间超过x,那么间隔也就无感了)

code

console.log('1');
setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).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');
        resolve();
    }).then(function() {
        console.log('12')
    })
})
//1,7,6,8,2,4,3,5,9,11,10,12
Promise.resolve().then(()=>{
  console.log('Promise1')
  setTimeout(()=>{
    console.log('setTimeout2')
  },0)
})
setTimeout(()=>{
  console.log('setTimeout1')
  Promise.resolve().then(()=>{
    console.log('Promise2')
  })
},0)
//Promise1,setTimeout1,Promise2,setTimeout2
/* 
• 一开始执行栈的同步任务(这属于宏任务)执行完毕,会去查看是否有微任务队列,上题中存在(有且只有一个),然后执行微任务队列中的所有任务输出 Promise1,同时会生成一个宏任务 setTimeout2
• 然后去查看宏任务队列,宏任务 setTimeout1 在 setTimeout2 之前,先执行宏任务 setTimeout1,输出 setTimeout1
• 在执行宏任务 setTimeout1 时会生成微任务 Promise2 ,放入微任务队列中,接着先去清空微任务队列中的所有任务,输出 Promise2
• 清空完微任务队列中的所有任务后,就又会去宏任务队列取一个,这回执行的是 setTimeout2
*/

总结:

for (const macroTask of macroTaskQueue) {
  handleMacroTask();
  
  for (const microTask of microTaskQueue) {
    handleMicroTask(microTask);
  }
}

猜你喜欢

转载自blog.csdn.net/m0_38073011/article/details/108450610