JS loop循环

本文详细解析了JavaScript中的事件循环过程,包括宏任务和微任务的区别,以及它们在setTimeout、Promise链和Node.js中的具体应用。通过实例分析展示了宏任务与微任务的执行顺序和特性。

loop循环

js代码执行过程中,除了依靠函数调用栈决定函数的执行顺序之外,还依靠任务队列搞定另外一些代码的执行,这整个过程我们称为事件循环过程

一个线程中,事件循环是唯一的,但是任务循环可以有多个,任务可以分为宏任务和微任务

宏任务

setTimeout

setImmediate

setInterval

I/O

ui render

微任务

promise.then

nextick

async await

MutationObserver

V8的垃圾回收过程

规则

1.在每一个宏任务中定义一个微任务队列,当一个宏任务执行完成之后就去查看其微任务队列是否为空,如果不为空则执行微任务队列,如果为空则执行下一个宏任务

2.promise里的直接执行,.then算下一大队列微任务;如果遇到return promise.resolve,则将后面的放到第三大队微任务

3.async 本行的内容立刻执行,后面的内容按顺序添加到微任务队列.

node中的事件循环

nodejs三个非常重要的阶段

1.timmer阶段 定时器时间到了先执行

1.I/O异常回调阶段

2.空闲预备阶段

2.poll轮询阶段 异步操作完了通知js主线程 如果有到时间的回到timmer阶段,执行完之后查看回调函数队列,有则check阶段,没有则等一会

3.check阶段

3.关闭事件的回调阶段

规则

1.process.nextTick()属于idle观察者,setImmediate()属于check观察者.在每一轮循环检查中,idle观察者先于I/O观察者,I/O观察者先于check观察者.

2.node 版本小于 11 的情况下,对于定时器的处理是:

若第一个定时器任务出队并执行完,发现队首的任务仍然是一个定时器,那么就将微任务暂时保存,直接去执行新的定时器任务,当新的定时器任务执行完后,再一一执行中途产生的微任务。

例题

题目一

题目

  const promise1 = new Promise((resolve, reject) => {
    console.log('promise1')
    resolve('resolve1')
  })
  const promise2 = promise1.then(res => {
    console.log(res)
  })
  console.log('1', promise1);
  console.log('2', promise2);

答案

'promise1'

'1' Promise{<resolved>: 'resolve1'}

'2' Promise{<pending>}

'resolve1'

分析

  • 从上至下,先遇到new Promise,执行该构造函数中的代码promise1
  • 碰到resolve函数, 将promise1的状态改变为resolved, 并将结果保存下来
  • 碰到promise1.then这个微任务,将它放入微任务队列
  • promise2是一个新的状态为pending的Promise
  • 执行同步代码1, 同时打印出promise1的状态是resolved
  • 执行同步代码2,同时打印出promise2的状态是pending
  • 宏任务执行完毕,查找微任务队列,发现promise1.then这个微任务且状态为resolved,执行它。

题目二

题目

setTimeout(() => {
  console.log('timer1');
  setTimeout(() => {
    console.log('timer3')
  }, 0)
}, 0)
setTimeout(() => {
  console.log('timer2')
}, 0)
console.log('start')
setTimeout(() => {
  console.log('timer1');
  Promise.resolve().then(() => {
    console.log('promise')
  })
}, 0)
setTimeout(() => {
  console.log('timer2')
}, 0)
console.log('start')

答案

'start'

'timer1'

'timer2'

'timer3'

'start'

'timer1'

'promise'

'timer2'

分析

这两个例子,看着好像只是把第一个定时器中的内容换了一下而已。

一个是为定时器timer3,一个是为Promise.then

但是如果是定时器timer3的话,它会在timer2后执行,而Promise.then却是在timer2之前执行。

你可以这样理解,Promise.then是微任务,它会被加入到本轮中的微任务列表,而定时器timer3是宏任务,它会被加入到下一轮的宏任务中。

题目三

题目

Promise.resolve().then(() => {
    console.log('promise1');
    const timer2 = setTimeout(() => {
      console.log('timer2')
    }, 0)
  });
  const timer1 = setTimeout(() => {
    console.log('timer1')
    Promise.resolve().then(() => {
      console.log('promise2')
    })
  }, 0)
  console.log('start');

答案

'start'

'promise1'

'timer1'

'promise2'

'timer2'

分析

<think>好的,我现在需要详细解释JavaScript事件循环机制的工作原理。首先,用户提供的引用资料已经涵盖了基本概念和部分运行机制,我需要整合这些信息,并确保结构清晰,符合用户的要求。 首先,根据引用[2],JavaScript是单线程的,这意味着它一次只能处理一个任务。这种设计避免了多线程操作DOM可能导致的竞争问题,但同时也带来了阻塞的风险。为了解决这个问题,JavaScript引入了事件循环机制,如引用[1]和[4]提到的,通过非阻塞I/O和异步处理来管理任务。 接下来,事件循环的基本结构需要明确。引用[4]指出,浏览器是多线程的,DOM事件、AJAX、setTimeout等由单独的线程处理。主线程执行同步代码,遇到异步任务时,将其交给对应的Web API处理。完成后,回调函数被放入任务队列,等待事件循环将它们推入调用栈执行。 任务队列的分类也很重要。引用[4]提到microtasks和macrotasks的区别。Microtasks包括Promise和MutationObserver,而macrotasks包括setTimeout、setInterval和I/O操作。事件循环在每一轮循环中会先处理所有microtasks,再处理一个macrotask,确保高优先级的任务优先执行。 然后,结合引用[3]和[5],需要说明事件循环的具体步骤:执行同步代码、处理microtasks队列、渲染UI(如果需要)、处理macrotasks队列中的一个任务,然后循环往复。例如,当多个setTimeout延时相同时,它们会被合并处理,但执行时仍按顺序进入队列。 最后,要确保回答中正确引用来源,并在末尾生成相关问题。需要检查LaTeX格式是否符合要求,特别是行内公式用$...$,独立公式用$$...$$。同时,保持中文回答,结构清晰,分点解释,帮助用户逐步理解机制。</think>### JavaScript事件循环机制详解 JavaScript的**事件循环(Event Loop)**是支撑其异步编程的核心机制,解决了单线程环境下的非阻塞问题。以下是其工作原理的分步解析: --- #### 一、核心概念 1. **单线程模型** JavaScript设计之初是单线程的,即只有一个主线程(Main Thread)执行代码[^2]。这意味着所有任务必须排队执行,无法同时处理多个操作。例如: ```javascript console.log("1"); setTimeout(() => console.log("2"), 0); console.log("3"); // 输出顺序:1 → 3 → 2 ``` 即使`setTimeout`延时为0,其回调仍会等待同步代码执行完毕。 2. **非阻塞的实现** 通过**事件循环**,JavaScript将耗时操作(如网络请求、定时器)交给浏览器或Node.js的其他线程处理,主线程继续执行后续代码。完成后,异步任务的回调被推入任务队列,等待执行[^1][^4]。 --- #### 二、事件循环的组成 1. **调用栈(Call Stack)** 用于执行同步代码,遵循后进先出(LIFO)原则。当遇到异步任务时,将其移交给**Web API**处理。 2. **任务队列(Task Queue)** - **宏任务队列(Macrotask Queue)**:包含`setTimeout`、`setInterval`、I/O操作等。 - **微任务队列(Microtask Queue)**:包含`Promise.then`、`MutationObserver`等。 3. **事件循环流程** 每次循环按以下顺序执行: ```plaintext 1. 执行调用栈中的同步代码。 2. 清空微任务队列中的所有任务。 3. 执行一个宏任务。 4. 重复循环。 ``` 例如: ```javascript setTimeout(() => console.log("宏任务"), 0); Promise.resolve().then(() => console.log("微任务")); console.log("同步代码"); // 输出顺序:同步代码 → 微任务 → 宏任务 ``` --- #### 三、关键机制 1. **微任务优先级高于宏任务** 每次事件循环中,微任务队列会在渲染前被完全清空。这意味着微任务可以阻塞渲染,而宏任务不会。 2. **浏览器渲染时机** 浏览器在每轮事件循环结束后,根据是否需要更新界面决定是否渲染(约60帧/秒)。 3. **Node.js与浏览器的差异** Node.js的事件循环分为多个阶段(如`timers`、`poll`、`check`),而浏览器环境更简化[^3]。 --- #### 四、示例分析 ```javascript console.log("Start"); setTimeout(() => console.log("Timeout"), 0); Promise.resolve().then(() => console.log("Promise")); console.log("End"); // 输出顺序: // Start → End → Promise → Timeout ``` **执行步骤**: 1. 同步代码`console.log`依次执行。 2. `setTimeout`回调进入宏任务队列。 3. `Promise.then`回调进入微任务队列。 4. 清空微任务队列,输出“Promise”。 5. 执行宏任务队列中的`setTimeout`回调。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值