普通视图

发现新文章,点击刷新页面。
昨天 — 2025年8月16日首页

npm发包自己的组件并安装更新版本应该如何做?

作者 林太白
2025年8月16日 10:42

npm发包组件

发布一个属于自己的npm包吧!接下来我们便使用Vue封装组件并发布到npm仓库

封装NPM组件-验证码

预览

npm-fabao1.png

1、创建账号注册登录

👉注册申请以及登录账号

官网

https://www.npmjs.com/

正常申请注册即可,选择 sign up 进入账户注册页面

npm-fabao2.png

2、创建vue3项目Tbcode

👉搭建项目

yarn create vite NexusCode --template vue

// 安装依赖
yarn 

👉创建组件

<template>
  <div class="necode">
    <div 
      class="codebox"
      :style="{
        'background': codeback,
        'width': width + 'px',
        'height': height + 'px'
      }"
      @click="getCode(length)"
    >
      {{ codevalue }}
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

// 接收父组件传递的 props
defineProps({
  value: {
    type: String,
    required: false,
  },
  length: {
    type: Number,
    default: 4,
    required: false,
  },
  back: {
    type: String,
    required: false,
  },
  width: {
    type: Number,
    default: 120,  // 默认宽度为120px
  },
  height: {
    type: Number,
    default: 40,   // 默认高度为40px
  }
});


const codelength = ref(4);

// 响应式变量
const codevalue = ref(''); // 验证码值
const codeback = ref('');  // 验证码背景色

// onMounted 是 Vue 3 的生命周期钩子,类似于 Vue 2 的 created
onMounted(() => {
  codelength.value=length?length:codelength.value;
  getCode(codelength.value); // 获取验证码
});

// 新增试用码-前端随机生成方法
const getCode = (row) => {
  // 随机背景颜色
  const r = Math.floor(Math.random() * 256);
  const g = Math.floor(Math.random() * 256);
  const b = Math.floor(Math.random() * 256);
  const rgb = `rgb(${r},${g},${b})`;
  codeback.value = rgb;

  const arrall = [
    'A', 'B', 'C', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
  ];
  let str = '';
  for (let i = 0; i < row; i++) {
    str += arrall[Math.floor(Math.random() * arrall.length)];
  }
  codevalue.value = str;
};
</script>

<style scoped>
.codebox {
  text-align: center;
  font-weight: 800;
  line-height: 40px;
  display: inline-block;
  float: left;
  cursor: pointer;
  font-size: 24px;
  color: #fff;
  border-radius: 4px;
}
</style>

👉配置package.json

私人化配置

"private": true,
这就代表私人的包

公共包配置(这里我们使用这个)

{
  "name": "tbcode",
  "version": "0.0.2",
  "files": [
    "dist"
  ],
  "module": "./dist/tbcode.es.js",
  "main": "./dist/tbcode.umd.js",
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/tbcode.es.ts",
      "require": "./dist/tbcode.umd.ts"
    },
    "./dist/style.css": {
      "import": "./dist/tbcode.css",
      "require": "./dist/tbcode.css"
    }
  },
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "vue": "^3.5.18"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^6.0.1",
    "vite": "^7.1.2"
  }
}

3、发布组件

👉版本名字

🍎名称规则

包名使用了@开头,一般使用@符号开头的包都是私有包,npm需要收费

加上--access public可直接发布组件,当成公有包,不需要支付费用

name:发布的包名,默认是上级文件夹名。不得与现在npm中的包名重复。包名不能有大写字母/空格/下滑线!

👉版本登录发布

Major version:大版本,代表破坏性变更。
Minor version:小版本,代表向后兼容的新功能。
Patch version:修订版本,代表 bug 修复和小改进。
🍎 版本发布
//查看当前用户是否登录
npm whoami 

//登陆
npm login 

// 或  npm addUser  

//部署
npm publish

👉发布名称冲突报错

下面就是名字被占用了

npm notice
npm notice package: Necode@0.0.2
npm notice Tarball Contents
npm notice 385B README.md
npm notice 169B dist/Necode.css
npm notice 2.0kB dist/necode.es.js
npm notice 1.3kB dist/necode.umd.js
npm notice 613B package.json
npm notice Tarball Details
npm notice name: Necode
npm notice version: 0.0.2
npm notice filename: Necode-0.0.2.tgz
npm notice package size: 2.1 kB
npm notice unpacked size: 4.4 kB
npm notice shasum: 6534df3352b5d299457dbff0b6e363b1b6ab6d4f
npm notice integrity: sha512-UZ1QGtymSFl73[...]rc1luwb77w/4Q==
npm notice total files: 5
npm notice
npm notice Publishing to 
  https://registry.npmjs.org/ with tag latest and default access
npm error code E400
npm error 400 Bad Request - PUT https://registry.npmjs.org/Necode - 
"Necode" is invalid for new packages
npm error A complete log of this run can be found in: 
C:\Users\admin\AppData\Local\npm-cache\_logs\2025-08-15T08_24_42_644Z-debug-0.log

👉更新名字,再次发布

npm publish提示信息如下

npm notice
npm notice package: tbcode@0.0.2
npm notice Tarball Contents
npm notice 385B README.md
npm notice 169B dist/Necode.css
npm notice 2.0kB dist/necode.es.js
npm notice 1.3kB dist/necode.umd.js
npm notice 613B package.json
npm notice Tarball Details
npm notice name: tbcode
npm notice version: 0.0.2
npm notice filename: tbcode-0.0.2.tgz
npm notice package size: 2.1 kB
npm notice unpacked size: 4.4 kB
npm notice shasum: 97d3dc40035c0e3fcbcb590704e7c0d531d9d16e
npm notice integrity: sha512-M4EQ/J8XRHZNT[...]CC0OwXVluzH8Q==
npm notice total files: 5
npm notice
npm notice Publishing to https://registry.npmjs.org/ with tag latest and default access
+ tbcode@0.0.2

搜索已经可以发现我们的npm包了

tbcode

👉更新版本

接下来我们传第二个版本包

添加一些关键词

// 发布一个小的补丁号版本
npm version patch
npm publish

这个时候已经可以看到我们的关键词和版本信息了

Keywords
vuereacttbcodelintaibai林太白
npm version patch -m "xx"
【
    patch增加一位补丁号,
    minor增加一位小版本号,
    major增加一位大版本号
 】

👉取消版本

// 舍弃某个版本的模块  24小时使用这个即可(使用)
npm unpublish tbcode@1.0.0

// 舍弃某个版本的模块
npm deprecate my-npm@"< 1.0.8" "critical bug fixed in v1.0.2"

// 如何撤销发布
npm --force unpublish my_npm

4、使用

发布到 npm 后,安装使用自己封装的组件

安装依赖

npm i tbcode

//或者
yarn add tbcode

项目引入使用

import tbCode from 'tbCode'
import 'tbcode/dist/style.css'

app.component('Tbcode', tbcode) 


<Tbcode/>

问题以及处理

👉组件样式不生效,

问题就出现在下面两点

(1)组件的导出方式配置不对

(2)使用时候引入没有

// 导出方式配置
"exports": {
    ".": {
      "import": "./dist/tbcode.es.js",
      "require": "./dist/tbcode.umd.js"
    },
    "./dist/style.css": {
      "import": "./dist/tbcode.css",
      "require": "./dist/tbcode.css"
    }
},


// 引入使用
import 'tbcode/dist/style.css'
昨天以前首页

事件循环(Call Stack、Task Queue、Event Loop)

作者 林太白
2025年8月11日 13:27

事件循环(Call Stack、Task Queue、Event Loop)

EventLoop(事件循环)认识

JavaScript 事件循环(Event Loop)是JavaScript运行时环境(比如浏览器或Node.js)的核心机制,允许 JavaScript 在执行异步代码时不阻塞主线程。这个机制使得 JavaScript 在执行长时间运行的任务(比如 I/O 操作、计时器等)时仍然能响应用户输入和执行其他任务

🍎作用

Event Loop是我们编写高效、非阻塞的 JavaScript 代码的核心。

Event Loop用于协调同步代码、异步代码以及各种任务的执行。

Event Loop使JavaScript这个单线程语言能够实现非阻塞的异步操作

确保程序不会因为阻塞操作(如长时间运行的计算)而导致用户界面或其他任务的冻结

🍎核心概念

事件循环的核心概念是 执行栈(Call Stack)、任务队列(Task Queue) 和 事件循环(Event Loop) 本身

执行栈 (Call Stack)----后进先出(LIFO模式-- Last In, First Out)

任务队列(Task Queue)---先进先出(FIFO模式 First In, First Out)

🍎解释

我们可以把Event Loop想象成一个永不停歇的循环,它不断地检查两个地方

一个是主线程的执行栈(Call Stack)

另一个是任务队列(Task Queue)

当执行栈清空时,Event Loop就会从任务队列中取出任务,放到执行栈中执行

eventloop1.png

🍎事件循环的工作原理

通过几个步骤来实现:

  • 任务队列(Task Queue) 事件循环不断检查任务队列,查看是否有任务需要执行。 任务可能是用户输入事件、I/O操作完成的通知、定时器的到期等。
  • 调用栈(Call Stack) 调用栈用于管理当前正在执行的代码。如果调用栈为空且任务队列中有任务, 事件循环将从任务队列中取出任务,并将其推送到调用栈中执行。
  • 非阻塞I/O操作: 在事件循环机制中,长时间运行的I/O操作(例如网络请求、文件读写)不会阻塞主线程, 而是将这些任务交给其他线程处理。 当I/O操作完成时,相关的回调函数会被放入任务队列中,等待事件循环去执行。

🍎JavaScript中的事件循环

在JavaScript中,事件循环在浏览器中或Node.js环境中都得到了广泛应用

JavaScript是单线程的,通过事件循环,可以处理大量的异步任务,而不会阻塞用户界面或其他任务

🍎异步编程与事件循环

异步编程通过回调、Promiseasync/await实现。事件循环确保这些异步操作按照正确的顺序执行,即使它们在代码中是非顺序的

微任务队列(Microtasks)

Promise的回调函数、MutationObserver的回调函数等会被放到微任务队列中。微任务的优先级高于宏任务,事件循环会在执行下一个宏任务前,先执行微任务队列中的所有任务

🍎优化异步代码

事件循环是实现非阻塞I/O操作、提高系统响应速度、优化用户体验的关键技术。在单线程环境中,它能有效地管理大量并发操作,写Event loop的时候我们需要注意到:

避免长时间运行的同步代码

长时间运行的同步代码会阻塞主线程,导致页面卡顿。如果遇到耗时操作,考虑将其拆分为多个小任务,或者使用Web Workers在后台线程中执行。

合理使用Promiseasync/await

Promiseasync/await是处理异步操作的强大工具,能够让代码更清晰、易维护。但滥用await可能会导致性能问题,例如在循环中频繁使用await

理解任务优先级

清楚宏任务和微任务的执行顺序,可以避免不必要的bug

进程(Process)和线程(Thread)

🍎进程

是计算机中运行的一个程序实例,代表着一个程序在内存中的执行。

每个进程都拥有独立的地址空间、代码、数据、文件描述符等资源,进程之间是相互独立的。

进程是操作系统管理的基本单位。

🍎线程

是进程中的一个执行单元,是程序执行的最小单位。

线程是进程内的一个执行流,指的是执行一段指令所需的时间。

一个进程可以包含一个或多个线程,这些线程共享进程的资源(如内存空间、文件描述符等)但每个线程有自己独立的执行栈。

线程之间的切换比进程之间的切换要轻便得多,因此,线程更适合用来进行多任务处理。

举个例子

比如我们打开手机上的一个app,就打开了一个进程。看到页面就开始了一个渲染线程,发消息开始了一个消息线程。这些线程共享微信进程的内存空间,各自执行不同的任务。

浏览器进程(Process)和线程(Thread)

浏览器也是多进程的应用程序,打开一个浏览器Tab页面时,就意味着开启了一个新的浏览器进程。

进程是独立的,所以即使其中一个Tab崩溃了,也不会影响到其他Tab的正常运行。

浏览器进程内部,有许多线程在协同工作,最终将网页内容展示出来。

我们理解Event Loop最密切相关的有以下几种线程

🍎HTTP请求线程(网络线程)

负责处理网络请求,下载前端(HTML、CSS、JavaScript)文件,或发送Ajax请求获取数据。

🍎JS引擎线程

负责解析和执行JavaScript代码。JavaScript是单线程的,这意味着在同一时间,JS引擎线程只能做一件事。

🍎渲染线程(GUI渲染线程)

负责解析HTML和CSS,构建DOM树和渲染树,并最终将页面绘制到屏幕上。当页面需要重绘或回流时,也是它来完成。

需要注意:JS引擎线程和渲染线程是互斥的。

意味着当JS引擎线程在执行JavaScript代码时,渲染线程会被挂起,无法进行页面的渲染。反之亦然。这就是为什么长时间运行的JavaScript代码会阻塞页面渲染,导致页面“卡死”的原因。而其他线程之间,比如HTTP请求线程和JS引擎线程,则可以并行工作,互不影响。

JavaScript的单线程特性与异步

🍎JavaScript单线程的原因

JavaScript被设计成单线程的,主要是为了避免DOM操作的复杂性。

如果JavaScript是多线程的,当多个线程同时操作同一个DOM元素时,就会出现竞态条件(Race Condition),导致不可预测的结果。

例如,一个线程要删除某个DOM元素,另一个线程要修改它,那么到底应该以哪个线程的操作为准呢?为了避免这种复杂性,JavaScript从诞生之初就被设计为单线程。

这意味着,JavaScript引擎在执行代码时,默认只开启一个线程工作。这个线程就是我们前面提到的JS引擎线程。负责从头到尾地执行JavaScript代码,一次只处理一个任务。

🍎单线程异步机制如何处理耗时操作-挂起

既然JavaScript是单线程的,那如何处理那些耗时很长的操作呢?网络请求(Ajax)、定时器(setTimeout、setInterval)或者文件读写?

如果这些操作都同步执行,在它们完成之前,JS引擎线程就会一直被阻塞,导致页面长时间无响应,用户体验极差。

为解决这个问题,JavaScript引入了异步机制。当JS引擎线程遇到异步代码时,它不会等待异步操作完成,而是会将其“挂起”,交给其他线程(比如浏览器提供的Web APIs)去处理,然后JS引擎线程继续执行后续的同步代码。当异步操作完成后,它会将一个“任务”放入任务队列(Task Queue)中,等待JS引擎线程空闲时再来处理。

🍎同步代码与异步代码的区别

同步代码

按照代码顺序,一行一行执行。只有前一行代码执行完毕才能执行下一行。如果某一行代码耗时很长,那么后续代码都会被阻塞。

console.log('Start');
alert('这是一个同步操作,会阻塞页面'); // 阻塞
console.log('End');

打印 'Start'

弹出警告框

只有点击确定后,才会打印 'End'。

在警告框弹出期间,页面是无法进行任何操作的。

异步代码

不会立即执行,而是会被“挂起”,等待某个条件满足(比如定时器时间到了,网络请求回来了)后,再将对应的回调函数放入任务队列,等待JS引擎线程空闲时执行。异步代码不会阻塞主线程的执行。

console.log('Start');
setTimeout(() => {
  console.log('Async operation finished');
}, 0);
console.log('End');

代码的执行顺序是

先打印 'Start',然后setTimeout被挂起,JS引擎线程继续执行,打印 'End'。最后,当JS引擎线程空闲时(即使setTimeout设置的时间是0毫秒),setTimeout的回调函数才会被执行,打印 'Async operation finished'。这就是异步的魅力,它让JavaScript在单线程的环境下也能处理复杂的、耗时的任务,而不会阻塞用户界面。

🍎任务队列的概念

任务队列是一个先进先出(FIFO)的队列,用于存放异步操作完成后需要执行的回调函数。当异步操作(比如setTimeout的计时结束,或者Ajax请求成功返回数据)满足了执行条件时,它们对应的回调函数并不会立即执行,而是会被放入这个任务队列中排队。JS引擎线程会在执行完所有同步代码后,才从任务队列中取出任务来执行。这个不断从任务队列中取出任务并执行的过程,就是Event Loop的核心所在。

宏任务(Macro-tasks)与微任务(Micro-tasks)

在JavaScript的异步世界里,任务被分成了两种类型:宏任务(Macro-tasks)和微任务(Micro-tasks)。这两种任务在Event Loop中有着不同的优先级和执行时机。

宏任务(Macro-tasks)包括:

  • setTimeout
  • setInterval
  • I/O操作(例如网络请求 ajax、文件读写)
  • UI渲染
  • setImmediate (Node.js环境特有)

微任务(Micro-tasks)包括:

  • Promise.then()Promise.catch()Promise.finally()
  • process.nextTick (Node.js环境特有)
  • MutationObserver (用于监听DOM变化)

Event Loop的执行顺序详解

宏任务和微任务是理解理解JavaScript异步编程的关键:

🍎执行宏任务,执行完毕=> 清空微任务=> 渲染页面(如果需要)=> 继续下一个宏任务

  1. 执行同步代码: 当JavaScript代码开始执行时,会首先执行所有的同步代码。这些同步代码可以被看作是当前宏任务的一部分。在执行过程中,如果遇到异步任务(无论是宏任务还是微任务),就会将其对应的回调函数放入相应的任务队列中。
  2. 清空微任务队列: 当所有同步代码执行完毕后,Event Loop并不会立即去执行宏任务队列中的任务。它会优先检查并清空微任务队列。意味着,所有在当前宏任务执行期间产生的微任务,都会在下一个宏任务开始之前被执行完毕。
  3. 页面渲染(可选): 在微任务队列清空之后,如果浏览器判断有必要进行页面渲染(比如DOM结构发生了变化,或者需要更新UI),它就会进行一次页面渲染。这一步是可选的,浏览器会根据实际情况决定是否进行渲染。
  4. 执行下一个宏任务: 页面渲染完成后,Event Loop会从宏任务队列中取出一个任务来执行。这个任务执行完毕后,又会重复步骤2,检查并清空微任务队列,然后再次进行页面渲染(如果需要),接着再从宏任务队列中取出下一个任务……如此循环往复,直到所有任务执行完毕。

async/await

🍎await关键字在Event Loop中的表现

async/await是ES2017引入的异步编程语法糖,它让异步代码看起来像同步代码一样,极大地提高了代码的可读性和可维护性。

await的底层实现依然是基于Promise和Event Loop的。

🍎await的本质:将后续代码推入微任务队列

当你使用await关键字时,它会暂停async函数的执行,直到await后面的Promise对象状态变为resolvedrejected。而await的精妙之处在于,它会将await后面的代码(即async函数中await表达式之后的代码)推入微任务队列。

这意味着,当await等待的Promise解决后,async函数中被暂停的代码会作为微任务被添加到微任务队列中,等待当前宏任务执行完毕,并且所有已有的微任务执行完毕后,才会轮到它执行。

🍎浏览器对await执行时间的优化

🌂早期版本async/await

在早期的async/await实现中,await 是基于传统的事件循环机制实现的。

await 后面的异步操作(如 Promise)被“暂停”并放入 微任务队列,而 await 本身会让代码的执行变得“同步化”

即浏览器会等待 await 后的异步任务执行完毕后,再继续执行下面的代码。导致在一些情况下,await 可能会带来比必要的更多的延时。

以前的await执行原理(已过时)

eventloop2.png

在旧的实现中,await会将await后面的代码(即async函数中await表达式之后的代码)作为一个微任务放入微任务队列。

await表达式本身,如果它等待的是一个Promise,那么这个Promisethen方法也会产生一个微任务。这可能导致一些复杂的执行顺序问题。

🌂导致的问题:

不必要的延时

如果 await 的异步操作已经很快执行完毕,浏览器仍然会等待当前执行周期的其他同步任务完成,导致一些不必要的延迟。

回调队列的拥塞

当多个异步任务并行执行时,await 后的代码会推迟到事件循环的微任务队列中,可能会造成回调的堆积和延迟。

🌂新版本

微任务队列优化

现在 await 在很多情况下会直接在当前事件循环内执行,而不需要推迟到下一个事件循环。这样,当异步操作快速完成时,await 可以更快地继续执行后续代码。

优先级调整

现代的JavaScript引擎会更智能地调度微任务,优先执行即将完成的异步任务,减少不必要的延迟。

现在的await执行顺序

eventloop3.png

现在,await的执行机制可以这样理解:当await一个Promise时,它会“阻塞”async函数的执行,直到Promise解决。一旦Promise解决,await会立即将async函数中await表达式后面的代码作为微任务添加到微任务队列中。而await表达式本身,可以被看作是立即执行的,它只是等待一个值。

结合代码示例深入分析await的执行流程:

让我们通过一个具体的例子来理解await的执行流程。假设我们有以下代码:

// async.js
async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}

async function async2() {
  console.log("async2");
}

console.log("script start");

setTimeout(function () {
  console.log("setTimeout");
}, 0);

async1();

new Promise(function (resolve) {
  console.log("promise1");
  resolve();
}).then(function () {
  console.log("promise2");
});

console.log("script end");

请你预测一下这段代码的输出顺序

我们来一步步分析:

  1. console.log("script start"): 首先执行同步代码,输出 script start
  2. setTimeout: 遇到setTimeout,将其回调函数放入宏任务队列。
  3. async1(): 调用async1函数。
  • console.log("async1 start"):async1函数内部,首先输出 async1 start
  • await async2(): 遇到awaitasync2函数被调用,输出 async2
  • await会暂停async1的执行,并将async1await后面的代码(即console.log("async1 end"))放入微任务队列。
  1. new Promise(...): 继续执行同步代码,遇到Promise
  • console.log("promise1")Promise构造函数是同步执行的,所以立即输出 promise1
  • resolve()Promise状态变为resolved
  • .then(...)then方法的回调函数被放入微任务队列。
  1. console.log("script end"): 继续执行同步代码,输出 script end

至此,所有的同步代码执行完毕。此时,微任务队列中有两个任务:

  1. async1await后面的代码 (console.log(async1 end"))
  2. Promise.then的回调 (console.log(promise2"))

根据Event Loop的执行顺序,接下来会清空微任务队列:

  1. 执行微任务队列: 先执行async1await后面的代码,输出 async1 end
  2. 执行微任务队列: 再执行Promise.then的回调,输出 promise2

微任务队列清空后,检查宏任务队列:

  1. 执行宏任务队列: 执行setTimeout的回调,输出 setTimeout

最终的输出顺序是:

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

这个例子完美地展示了async/await在Event Loop中的执行机制,以及微任务的优先级高于宏任务的特性。

常见误区

理解了Event Loop的原理,我们再来看一些实际场景和常见的误区

🍎经典题

Event Loop是前端常客,尤其是关于输出顺序的题目。除了上面async/await的例子,我们再来看一个经典的题目:

// 1.js
console.log("script start");

setTimeout(function () {
  console.log("setTimeout");
}, 0);

Promise.resolve()
  .then(function () {
    console.log("promise1");
  })
  .then(function () {
    console.log("promise2");
  });

console.log("script end");

做一下这段代码的输出顺序以后我们分析一下

同步代码=> 微任务队列 => 下一个宏任务

  1. console.log("script start"): 首先执行同步代码,输出 script start
  2. setTimeout: 遇到setTimeout,将其回调函数放入宏任务队列。
  3. Promise.resolve().then(...)Promise.resolve()会立即返回一个已解决的Promise。它的第一个.then回调函数被放入微任务队列。
  4. console.log("script end"): 继续执行同步代码,输出 script end

至此,所有同步代码执行完毕。此时微任务队列中有:promise1的回调。

  1. 执行微任务队列: 执行promise1的回调,输出 promise1。注意,promise1的回调执行完毕后,它返回的Promise会立即resolve,因此其后面的.then回调(promise2)会立即被放入微任务队列。
  2. 继续执行微任务队列: 执行promise2的回调,输出 promise2

微任务队列清空。检查宏任务队列:

  1. 执行宏任务队列: 执行setTimeout的回调,输出 setTimeout

最终的输出顺序是:

script start
script end
promise1
promise2
setTimeout

注意微任务的优先级。即使setTimeout的延迟时间设置为0,它也必须等待当前宏任务中的所有微任务执行完毕后才能执行。

🍎错误理解与纠正

  1. 误区:setTimeout(fn, 0)会立即执行。纠正:setTimeout(fn, 0)表示将fn放入宏任务队列,等待主线程空闲且所有微任务执行完毕后,在下一个宏任务阶段执行。它并不能保证立即执行,只是表示“尽快”执行。

  2. 误区:async/await是同步的。纠正:async/await只是语法糖,它让异步代码看起来像同步代码,但其本质仍然是基于Promise的异步操作,并且await后面的代码会被推入微任务队列,遵循Event Loop的规则。

  3. 误区:只要是异步代码,就一定会比同步代码后执行。纠正: 这句话不完全正确。异步代码的回调函数确实会在同步代码执行完毕后才执行,但微任务的优先级高于宏任务。所以,在同步代码执行完毕后,会先执行所有微任务,再执行宏任务。

❌
❌