Maison  >  Article  >  interface Web  >  Une brève analyse de Buffer dans Node.js et une présentation de la boucle d'événements

Une brève analyse de Buffer dans Node.js et une présentation de la boucle d'événements

青灯夜游
青灯夜游avant
2022-05-18 20:30:462480parcourir

Cet article vous amènera à comprendre le Buffer dans Node et à parler de la boucle d'événements du navigateur. J'espère qu'il sera utile à tout le monde !

Une brève analyse de Buffer dans Node.js et une présentation de la boucle d'événements

Buffer


binary of data

  • all contenu dans l'ordinateur: texte, chiffres, images, audio et vidéo finira par utiliser le système binaire pour exprimer

  • JS peut traiter directement des données très intuitives : comme les chaînes, que nous affichons habituellement aux utilisateursJS可以直接去处理非常直观的数据:比如字符串,我们通常展示给用户的也是这些内容

  • 但你可能会以为JS也能够处理图片

    • 事实上在网页端,图片一直是交给浏览器来处理的
    • JS或者HTML,只是负责告诉浏览器图片的地址
    • 浏览器负责发送请求获取这个图片,并且最终将这个图片给渲染出来
  • 但是对于服务端来说是不一样的

    • 服务端要处理的本地文件类型相对较多
    • 比如某一个保存文本的文件并不是使用utf-8进行编码的,而是用GBK,那么我们必须读取到他们的二进制数据,再通过GKB转换成对应的文字
    • 比如我们需要读取的是一张图片数据(二进制),再通过某些手段对图片数据进行二次的处理(裁剪、格式转换、旋转、添加滤镜),Node中有一个名为sharp的库,就是负责读取图片或者传入图片的Buffer对其再进行处理的
    • 比如在Node中通过TCP建立长连接,TCP传输的是字节流,我们需要将数据转成字节再进行传入,并且需要知道传输字节的大小(客户端需要根据大小来判断读取多少内容)

Buffer和二进制

  • 我们会发现,对于前端开发来说,通常很少会和二进制打交道,但是对于服务器端来说,为了实现很多功能,我们必须直接去操作其二进制的数据

  • 所以Node为了可以方便开发者完成更多功能,提供给了我们一个名为Buffer的类,并且他是全局的

  • 我们前面说过,Buffer中存储的是二进制数据,那么到底是如何存储的呢?

    • 我们可以将Buffer看成是一个存储二进制的数组
    • 这个数组中的每一项,可以保存8位二进制:00000000,刚好是一个字节
  • 为什么是8位呢?

    • 在计算机中,很少的情况我们会直接操作一位二进制,因为一位二进制存储的数据是非常有限的
    • 所以通常会将8位合在一起作为一个单元,这个单元称之为一个字节(byte
    • 也就是说 1 byte = 8 bit1kb = 1024 byte1M = 1024kb1 G = 1024 M
    • 比如很多编程语言中的int类型是4个字节,long类型是8个字节
    • 比如TCP传输的是字节流,在写入和读取时都需要说明字节的个数
    • 比如RGB的值分别都是255,所以本质上在计算机中都是用一个字节存储的

Buffer和字符串

  • Buffer相当于是一个字节的数组,数组中的每一项对于一个字节的大小

  • 如果我们希望将一个字符串放入到Buffer中,是怎么样的过程呢?

    • 将字符串直接传入Buffer类中,然后再创建buffer实例
    • 英文字符串有个特点,每一个字符对应一个字节的二进制编码
const message = 'Hello'
// 使用new关键字创建buffer实例,但这种创建方法已经过期了
const buffer = new Buffer(message)
console.log(buffer); // <Buffer 48 65 6c 6c 6f>
console.log(buffer.toString()); // Hello

中文字符串的编解码

  • buffer的默认编码是utf-8,所以在下列代码中,Buffer类是使用了utf-8编码对我们的字符串进行编码,使用的也是utf-8对我们的字符串进行解码
  • 中文字符串有个特点,在utf-8编码中,一个文字对应3个字节的二进制编码
const message = &#39;你好啊&#39;
// 使用Buffer.from对我们的字符串进行解码
const buffer = Buffer.from(message)
console.log(buffer); // <Buffer e4 bd a0 e5 a5 bd e5 95 8a>
// buffer实例中有个toString方法可以对编码进行解码
console.log(buffer.toString()); // &#39;你好啊&#39;
  • 那如果编码和解码用的是不同形式的编码结果会怎么样呢?

    • 毫无疑问,解码出来的东西并不是我们原先编码的字符串
const message = &#39;你好啊&#39;
const buffer = Buffer.from(message, &#39;utf16le&#39;)
console.log(buffer); // <Buffer 60 4f 7d 59 4a 55>
console.log(buffer.toString()); // `O}YJU

Buffer的其他创建方式

创建buffer的方式有很多,我们这里可以通过alloc的方式创建Buffer

🎜🎜Mais vous pensez peut-être que JS peut aussi traiter des images🎜🎜 🎜En fait, sur la page Web, l'image a toujours été traitée par le navigateur🎜🎜JS ou HTML est uniquement responsable d'indiquer au navigateur l'adresse de l'image🎜🎜Le navigateur est responsable Envoyez une requête pour obtenir cette image, et enfin restituez cette image🎜🎜🎜🎜Mais c'est différent pour le côté serveur🎜🎜🎜Le côté serveur doit traiter relativement de nombreux types de fichiers locaux🎜🎜Par exemple, un A Le fichier qui enregistre le texte n'est pas codé en utilisant utf-8, mais en utilisant GBK, nous devons alors lire leurs données binaires puis les convertir en texte correspondant 🎜🎜Par exemple, quoi nous devons lire des données d'image (binaires), puis effectuer un traitement secondaire sur les données d'image par certains moyens (recadrage, conversion de format, rotation, ajout de filtres). Node a une bibliothèque nommée sharp est chargé de lire les images ou de transmettre le Buffer des images puis de les traiter. Par exemple, dans Node Établissez une longue connexion via TCP. TCP transmet un flux d'octets. Nous devons convertir les données en octets avant de les transmettre, et nous devons connaître la taille des octets transmis (le client doit juger en fonction de la taille de la quantité de contenu à lire)🎜🎜🎜🎜🎜Buffer et binaire 🎜🎜🎜🎜🎜🎜Nous constaterons que pour le développement front-end, nous traitons généralement rarement du binaire, mais pour le côté serveur Said, afin d'implémenter de nombreuses fonctions, nous doit exploiter directement ses données binaires 🎜🎜🎜🎜Ainsi, afin de permettre aux développeurs de remplir plus de fonctions, Node nous fournit un fichier appelé la classe de Buffer , et il est global🎜🎜🎜🎜Nous avons déjà dit que les données binaires sont stockées dans Buffer, alors comment sont-elles stockées ? 🎜🎜🎜Nous pouvons considérer Buffer comme un tableau qui stocke des binaires 🎜🎜Chaque élément de ce tableau peut enregistrer 8 bits de binaire : 00000000, ce qui correspond exactement à un octet🎜 🎜🎜🎜🎜Pourquoi sont-ils en 8 bits ? 🎜🎜🎜Dans les ordinateurs, il y a très peu de cas où nous opérons directement un bit binaire, car les données stockées dans un bit binaire sont très limitées🎜🎜Donc 8 bits sont généralement regroupés comme une unité, et cette unité est appelée Un octet (byte) 🎜🎜C'est-à-dire, 1 octet = 8 bits, 1ko = 1024 octet, 1M = 1024ko, <code>1 G = 1024 M🎜🎜Par exemple, le type int dans de nombreux langages de programmation est 4 octets, long Le type est 8 octets🎜🎜Par exemple, TCP transmet un flux d'octets, et le nombre d'octets doit être spécifié lors de l'écriture et de la lecture 🎜. 🎜Par exemple, les valeurs de RGB sont chacune 255, elles sont donc essentiellement stockées dans un octet dans l'ordinateur🎜🎜🎜 🎜🎜Buffer et string🎜🎜🎜🎜🎜🎜Buffer est équivalent à un tableau d'octets, chaque élément du tableau a une taille d'un octet🎜🎜🎜🎜Si nous voulons convertir un caractère Quel est le processus de mise d’une chaîne dans un Buffer ? 🎜🎜🎜Passez la chaîne directement dans la classe Buffer, puis créez une instance buffer🎜🎜Les chaînes anglaises ont une caractéristique, chaque caractère correspond à un octet d'encodage binaire🎜🎜 ul>
// 其可以指定我们buffer的位数,比如这里传递进去的是8,那么创建出来的buffer就有8个元素,且每个元素对应的二进制数都是0
const buffer = Buffer.alloc(8)
console.log(buffer); // <Buffer 00 00 00 00 00 00 00 00>
// 赋值为十进制数字的话,buffer会帮我们转化为16进制数字再写入到对应的位置
buffer[0] = 88 
// 在js中,以0x开头的就表示为16进制的数字
buffer[1] = 0x88
console.log(buffer); // <Buffer 58 88 00 00 00 00 00 00>
🎜🎜🎜Encodage et décodage de chaînes chinoises🎜🎜🎜🎜🎜 L'encodage par défaut de buffer est utf-8, donc dans le code suivant, La classe Buffer utilise l'encodage utf-8 pour encoder nos chaînes, et utf-8 est également utilisé pour décoder nos chaînes. Les chaînes chinoises ont une fonctionnalité particulière, en encodage utf-8, un texte correspond à 3<.> octets de codage binaire🎜<pre class="brush:js;toolbar:false;">const fs = require(&amp;#39;fs&amp;#39;) fs.readFile(&amp;#39;./a.txt&amp;#39;, (err, data) =&gt; { console.log(data); // &lt;Buffer e5 93 88 e5 93 88&gt; })</pre>🎜🎜🎜Que se passera-t-il si le codage et le décodage utilisent des formes de codage différentes ? 🎜🎜🎜Il ne fait aucun doute que la chose décodée n'est pas la chaîne que nous avons codée à l'origine🎜🎜<pre class="brush:js;toolbar:false;">const fs = require(&amp;#39;fs&amp;#39;) // encoding表示解码所用的字符编码,编码默认为utf-8 fs.readFile(&amp;#39;./a.txt&amp;#39;, { encoding: &amp;#39;utf-8&amp;#39; }, (err, data) =&gt; { console.log(data); // 哈哈 })</pre>🎜🎜🎜Autres façons de créer Buffer🎜🎜🎜🎜Créer un <code>buffer Il existe de nombreuses façons, ici nous pouvons créer un Buffer via alloc🎜
  • 我们可以直接对buffer实例以数组的形式对每一位进行修改

    • 如果修改的是一个十进制数字,那它会自动帮助我们转化成16进制的数字
    • 如果修改的是一个十六进制数字,那么就会直接写入
// 其可以指定我们buffer的位数,比如这里传递进去的是8,那么创建出来的buffer就有8个元素,且每个元素对应的二进制数都是0
const buffer = Buffer.alloc(8)
console.log(buffer); // <Buffer 00 00 00 00 00 00 00 00>
// 赋值为十进制数字的话,buffer会帮我们转化为16进制数字再写入到对应的位置
buffer[0] = 88 
// 在js中,以0x开头的就表示为16进制的数字
buffer[1] = 0x88
console.log(buffer); // <Buffer 58 88 00 00 00 00 00 00>

Buffer和文件操作

1、文本文件

  • 如果未指定字符编码,则不进行解码,直接返回原始的 buffer,也就是文件内容结果utf-8编码后的二进制数
const fs = require(&#39;fs&#39;)
fs.readFile(&#39;./a.txt&#39;, (err, data) => {
  console.log(data); // <Buffer e5 93 88 e5 93 88>
})
  • 编码和解码用的都是utf-8,则可以得到文件中正确的内容
const fs = require(&#39;fs&#39;)
// encoding表示解码所用的字符编码,编码默认为utf-8
fs.readFile(&#39;./a.txt&#39;, { encoding: &#39;utf-8&#39; }, (err, data) => {
  console.log(data); // 哈哈
})
  • 如果编码和解码所用的字符编码不同,则最终读取出来的内容会乱码
const fs = require(&#39;fs&#39;)
// 编码用的是utf16le字符编码,解码使用的是utf-8格式,肯定是解不是正确的内容的
fs.readFile(&#39;./a.txt&#39;, { encoding: &#39;utf16le&#39; }, (err, data) => {
  console.log(data); // 鏥袓
})
// 以上代码和下面代码类似
const msg = &#39;哈哈&#39;
const buffer = Buffer.from(msg, &#39;utf-8&#39;)
console.log(buffer.toString(&#39;utf16le&#39;)); // 鏥袓

2、图片文件

  • 对图片编码进行拷贝,达到复制图片的目的

    • 读取图片的时候不要指定encoding属性,因为字符编码只有在读取文本文件的时候才有用
const fs = require(&#39;fs&#39;)

fs.readFile(&#39;./logo.png&#39;, (err, data) => {
  console.log(data); // 打印出来的是图片文件对应的二进制编码
  
  // 我们还可以将图片编码写入到另一个文件当中,相当于我们将该图片拷贝了一份
  fs.writeFile(&#39;./bar.png&#39;, data, err => {
    console.log(err); 
  })
})
  • 对图片进行翻转、裁剪等操作,可以使用sharp这个库
const sharp = require(&#39;sharp&#39;)

// 将logo.png这张图片裁剪成200x300后拷贝到文件bax.png中
sharp(&#39;./logo.png&#39;)
  .resize(200, 300)
  .toFile(&#39;./bax.png&#39;, (err, info) => {
    console.log(err);
  })

// 还可以将图片文件先转为buffer,然后在写入到文件中,也可以实现拷贝图片的目的
sharp(&#39;./logo.png&#39;)
  .resize(300, 300)
  .toBuffer()
  .then(data => {
    fs.writeFile(&#39;./baa.png&#39;, data, err => {
      console.log(err);
    })
  })

Buffer的创建过程

  • 事实上我们创建Buffer时,并不会频繁的向操作系统申请内存,它会默认先申请一个8 * 1024 个字节大小的内存,也就是8kb
  • 等到内存不够或者快用完的时候才会去申请新的内存

事件循环和异步IO


什么是事件循环?

  • 事件循环是什么?

    • 事实上我把事件循环理解成我们编写的JS和浏览器或者Node之间的一个桥梁
  • 浏览器的事件循环是一个我们编写的JS代码和浏览器API调用(setTimeoutAJAX监听事件等)的一个桥梁,桥梁之间通过回调函数进行沟通
  • Node的事件循环是一个我们编写的JS代码和系统调用(file systemnetwor等)之间的一个桥梁,,桥梁之间也是通过回调函数进行沟通的

Une brève analyse de Buffer dans Node.js et une présentation de la boucle dévénements

进程和线程

进程和线程是操作系统中的两个概念:

  • 进程(process):计算机已经运行的程序
  • 线程(thread):操作系统能够运行运算调度的最小单位,所以CPU能够直接操作线程

听起来很抽象,我们直观一点解释:

  • 进程:我们可以认为,启动一个应用程序,就会默认启动一个进程(也可能是多个进程)
  • 线程:每一个进程中,都会启动一个线程用来执行程序中的代码,这个线程被称之为主线程
  • 所以我们也可以说进程是线程的容器

再用一个形象的例子解释

  • 操作系统类似于一个工厂
  • 工厂中有很多车间,这个车间就是进程
  • 每个车间可能有一个以上的工人在工厂,这个工人就是线程

多进程多线程开发

操作系统是如何做到同时让多个进程(边听歌、边写代码、边查阅资料)同时工作呢?

Une brève analyse de Buffer dans Node.js et une présentation de la boucle dévénements

  • 这是因为CPU的运算速度非常快,他可以快速的在多个进程之间迅速的切换
  • 当我们的进程中的线程获取到时间片时,就可以快速执行我们编写的代码
  • 对于用户来说是感受不到这种快速的切换的

Une brève analyse de Buffer dans Node.js et une présentation de la boucle dévénements

浏览器和JavaScript

  • 我们经常会说JavaScript是单线程的,但是JS的线程应该有自己的容器进程:浏览器或者Node

  • 浏览器是一个进程吗,它里面只有一个线程吗?

    • 目前多数的浏览器其实都是多进程的,当我们打开一个tab页面时就会开启一个新的进程,这是为了防止一个页面卡死而造成所有页面无法响应,整个浏览器需要强制退出
    • 每个进程中又有很多的线程,其中包括执行JavaScript代码的线程
  • 但是JavaScript的代码执行是在一个单独的线程中执行的

    • 这就意味着JS的代码,在同一时刻只能做一件事
    • 如果这件事是非常耗时的,就以为这当前的线程就会被阻塞

JavaScript的执行过程

函数要被压入函数调用栈中后才会被执行,下面我们来分析下代码的执行过程

const message = &#39;Hello World&#39;

console.log(message);

function sum(num1, num2) {
  return num1 + num2
}

function foo() {
  const result = sum(20, 30)
  console.log(result);
}

foo()
  • 我们JS的代码其实也可以像其它编程语言一样可以看成是在main函数中执行的
  • 那么首先我们要将main函数压入函数调用栈中
  • 定义变量message
  • 执行log函数,log函数会被放入到函数调用栈中,执行完后出栈
  • 调用foo函数,foo函数被压入函数调用栈中,但是执行过程中又需要调用sum函数
  • 所以sum函数会被压入到函数调用栈中,sum函数执行完毕后出栈
  • 此时foo函数也得到了sum函数返回的值,并执行了赋值操作,但又遇到了log函数
  • 所以又要将log函数压入到调用栈,log函数被执行完毕,出栈后foo函数也执行完毕,foo函数出栈
  • foo函数执行完后,整个js代码执行完毕,main函数出栈

浏览器的事件循环

如果在执行JS代码的过程中,有异步操作呢?

  • 比如中间我们插入了一个setTimeout的函数调用
  • 那么setTimeout这个函数被放入到调用栈中,执行会立即结束,并不会阻塞后续代码的执行

那么,往setTimeout函数里面传入的函数(我们称之为timer函数),会在什么时候被执行呢?

  • 事实上,setTimeout是调用了web api,浏览器会提前会将回调函数存储起来,在合适的时机,会将timer函数加入到一个事件队列中
  • 事件队列中的函数,会被放入到函数调用栈中,在调用栈中被执行

为什么setTimeout不会阻塞代码的执行呢?就是因为浏览器里面维护了一个非常非常重要的东西——事件循环

  • 浏览器中会通过某种方式帮助我们保存setTimeout中的回调函数的,比较常用的方法就是保存到一个红黑树里面

  • 等到setTimeout定时器时间到达的时候,它就会将我们的timer回调函数从保存的地方取出来并放入到事件队列里面

  • 事件循环一旦发现我们的队列中有东西了,并且当前函数调用栈是空的,其它同步代码也执行完之后,就会将我们队列中的回调函数依次出列放入到函数调用栈中执行(队列中前一个函数出栈后,下一个函数才会入栈)

当然事件队列中不一定只有一个事件,比如说在某个过程中用户点击了浏览器当中的某个按钮,我们可能对这个按钮的点击做了一个监听,对应了一个回调函数,那个回调函数也会被加入到我们的队列里面的,执行顺序按照它们在事件队列中的顺序执行。还有我们发送ajax请求的回调,也是加入到事件队列里面的

总结:其实事件循环是一个很简单的东西,它就是在某一个特殊的情况下,需要去执行某一个回调的时候,它就把提前保存好的回调塞入事件队列里面,事件循环再给它取出来放入到函数调用栈中

Une brève analyse de Buffer dans Node.js et une présentation de la boucle dévénements

宏任务与微任务

但是事件循环中并非只维护一个队列,事实上是有两个队列,而且队列中的任务执行一定会等到所有的script都执行完毕后

  • 宏任务队列(macrotask queue):ajaxsetTimeoutsetIntervalDOM监听、UI Rendering
  • 微任务队列(microtask queue):Promisethen回调、Mutation Observer APIqueueMicrotask()

那么事件循环对于两个队列的优先级是怎么样的呢?

  • main script中的代码优先执行(编写的顶层script代码)
  • 在执行任何一个宏任务之前(不是队列,是一个宏任务),都会先查看微任务队列中是否有任务需要执行
    • 也就是宏任务执行之前,必须保证微任务队列是空的
    • 如果不为空,那么就优先执行微任务队列中的任务(回调)

面试题

考点:main stciptsetTimeoutPromisethenqueueMicrotask

setTimeout(() => {
  console.log(&#39;set1&#39;);4
  new Promise(resolve => {
    resolve()
  }).then(resolve => {
    new Promise(resolve => {
      resolve()
    }).then(() => {
      console.log(&#39;then4&#39;);
    })
    console.log(&#39;then2&#39;);
  })
})

new Promise(resolve => {
  console.log(&#39;pr1&#39;);
  resolve()
}).then(() => {
  console.log(&#39;then1&#39;);
})

setTimeout(() => {
  console.log(&#39;set2&#39;);
})

console.log(2);

queueMicrotask(() => {
  console.log(&#39;queueMicrotask&#39;);
})

new Promise(resolve => {
  resolve()
}).then(() => {
  console.log(&#39;then3&#39;);
})

// pr1
// 2
// then1
// queueMicrotask
// then3
// set1
// then2
// then4
// set2
  • setTimeout会立即压入函数调用栈,执行完毕后立即出栈,其timer函数被放入到宏任务队列中

  • 传入Promise类的函数会被立即执行,其并不是回调函数,所以会打印出pr1,并且由于执行了resolve方法,所以该Promise的状态会立即变为fulfilled,这样then函数执行的时候其对应的回调函数就会被放入到微任务队列中

  • 又遇到了一个setTimeout函数,压栈出栈,其timer函数会被放入到宏任务队列中

  • 遇到console.log语句,函数压栈后执行打印出了2,然后出栈

  • 这里通过queueMicrotask绑定了个函数,该函数会被放入到微任务队列中

  • 又遇到了new Promise语句,但是其立即就将promise的状态改为了fulfilled,所以then函数对应的回调也被放入到了微任务队列中

  • 由于同步脚本代码已经执行完毕,现在事件循环开始要去把微任务队列和宏任务对垒的任务按照优先级顺序放入到函数调用栈中执行了,注意:微任务的优先级比宏任务高,每次想要执行宏任务之前都要看看微任务队列里面是否为空,不为空则需要先执行微任务队列的任务

  • 第一个微任务是打印then1,第二个微任务是打印queueMicrotask,第三个微任务是打印then3,执行完毕后,就开始去执行宏任务

  • 第一个宏任务比较复杂,首先会打印set1,然后执行了一个立即变换状态的new promise语句,其then回调会被放入到微任务队列中,注意现在微任务队列可不是空的,所以需要执行优先级较高的微任务队列,相当于该then回调被立即执行了,又是相同的new Promise语句,其对应的then对调被放入到微任务队列中,注意new Promise语句后面还有一个console函数,该函数会在执行完new Promise语句后立即执行,也就是打印then2,现在微任务对垒还是有一项任务,所以接下来就是打印then4。目前为止,微任务队列已经为空了,可以继续执行宏任务队列了

  • 所以接下里的宏任务set2会被打印,宏任务执行完毕

  • 整个代码的打印结果是:pr1 -> 2 -> then1 -> queueMicrotask -> then3 -> set1 -> then2 -> then4 -> set2

面试题

考点:main scriptsetTimeoutPromisethenqueueMicrotaskawaitasync

知识补充:async、await是Promise的一个语法糖,在处理事件循环问题时

  • 我们可以将await关键字后面执行的代码,看做是包裹在new Promise((resolve,rejcet) => { 函数执行 })中的代码
  • await语句后面的代码,可以看做是上一个Promise中的then(res => {函数执行})中的代码
async function async1() {
  console.log(&#39;async1 start&#39;);
  await async2()
  console.log(&#39;async1 end&#39;);
}

async function async2() {
  console.log(&#39;async2&#39;);
}

console.log(&#39;script start&#39;);

setTimeout(() => {
  console.log(&#39;setTimeout&#39;);
}, 0)

async1()

new Promise(resolve => {
  console.log(&#39;promise1&#39;);
  resolve()
}).then(() => {
  console.log(&#39;promise2&#39;);
})

console.log(&#39;script end&#39;);

// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout
  • 一开始都是函数的定义,不需要压入函数调用栈中执行,直到遇到第一个console语句,压栈后执行打印script start后出栈

  • 遇到第一个setTimeout函数,其对应的timer会被放入到宏任务队列中

  • async1函数被执行,首先打印出async1 start,然后又去执行await语句后面的async2函数,因为前面也说了,将await关键字后面的函数看成是new Promise里面的语句,这个函数是会被立即执行的,所以async2会被打印出来,但该await语句后面的代码相当于是放入到then回调中的,也就是说console.log('async1 end')这行代码被放入到了微任务队列里

  • Le code continue de s'exécuter et rencontre une nouvelle instruction Promise, donc promise1 est immédiatement imprimé et la fonction dans le rappel then est placée dans la file d'attente des microtâchespromise1,then回调中的函数被放入到了微任务队列里面去

  • 最后一个console函数执行打印script end,同步代码也就执行完毕了,事件循环要去宏任务和微任务队列里面执行任务了

  • 首先是去微任务队列,第一个微任务对应的打印语句会被执行,也就是说async1 end会被打印,然后就是promise2被打印,此时微任务队列已经为空,开始去执行宏任务队列中的任务了

  • timer函数对应的setTimeout会被打印,此时宏任务也执行完毕,最终的打印顺序是:script start -> async1 start -> async2 -> promise1 -> script end -> async1 end -> promise2 -> setTimeout

La dernière console La fonction l'exécution imprime la fin du script et le code de synchronisation est terminé. La boucle d'événements va aux files d'attente de tâches macro et de micro-tâches pour exécuter les tâches

Tout d'abord, accédez à la file d'attente des micro-tâches. task L'instruction print correspondant à la tâche sera exécutée, c'est-à-dire que async1 end sera imprimé, puis promise2 sera imprimé à ce moment-là. la file d'attente est vide et l'exécution commence. Les tâches dans la file d'attente des tâches macro 🎜🎜🎜🎜fonction timer correspondant à setTimeout seront imprimées à ce moment-là, l'ordre d'impression final est : script start -&gt. ; async1 start -> async2 -&gt ; promise1 -> async1 end -> setTimeout🎜🎜🎜🎜Pour plus de connaissances sur les nœuds, veuillez visiter : 🎜nodejs 🎜! 🎜

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer