Rumah  >  Artikel  >  hujung hadapan web  >  Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

青灯夜游
青灯夜游ke hadapan
2021-09-30 10:22:382272semak imbas

Artikel ini menggunakan gabungan gambar dan teks untuk membantu anda memahami gelung acara dalam Nodejs Saya harap ia akan membantu semua orang!

Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

Teks penuh berikut ialah 7,000 patah perkataan, sila baca apabila anda mempunyai pemikiran yang jelas dan penuh tenaga. Saya jamin anda tidak akan melupakannya untuk masa yang lama selepas anda memahaminya. [Pembelajaran yang disyorkan: "tutorial nodejs"]

Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

Gelung acara nod

Bahasa yang digunakan di bahagian bawah Node, libuv, ialah bahasa c. Ia digunakan untuk mengendalikan sistem pengendalian asas dan merangkum antara muka sistem pengendalian. Gelung peristiwa Node juga ditulis menggunakan libuv, jadi kitaran hayat Node masih berbeza daripada pelayar.

Oleh kerana Node berurusan dengan sistem pengendalian, gelung peristiwa adalah agak kompleks dan mempunyai beberapa API uniknya sendiri.
Gelung acara mempunyai beberapa perbezaan halus dalam sistem pengendalian yang berbeza. Ini akan melibatkan pengetahuan tentang sistem pengendalian, yang tidak disenaraikan buat masa ini. Kali ini kami hanya memperkenalkan proses operasi Node dalam utas utama JS. Urutan lain Node tidak akan dikembangkan buat masa ini.

Rajah Gelung Peristiwa

Rajah yang dipersetujui bukan rahsia. Sebaik sahaja anda memahami gambar di bawah, anda akan mempelajari gelung acara.

Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

Rajah Gelung Peristiwa

Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

Rajah Gelung Peristiwa - Struktur

Untuk memberi setiap orang Melihat keadaan keseluruhan, mula-mula siarkan gambar rajah struktur direktori di hadapan:

Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

Direktori

Kemudian mari kita bincangkannya secara terperinci

Benang utama

Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

Benang utama

Dalam gambar di atas, maksudnya daripada beberapa blok warna :

  • main: Mulakan fail entri dan jalankan fungsi utama
  • event loop: Semak sama ada hendak memasuki gelung acara
    • Semak sama ada terdapat sebarang acara yang belum selesai dalam urutan lain Memproses item
    • Semak sama ada tugasan lain masih berjalan (seperti pemasa, operasi membaca fail dan tugasan lain telah selesai)
    • Dalam situasi di atas , masukkan gelung acara dan jalankan tugas lain
      Proses gelung acara: ikuti proses daripada pemasa untuk menutup panggilan balik dan berjalan dalam bulatan. Pergi ke gelung acara untuk melihat sama ada ia telah tamat.
  • over: Semua perkara telah selesai, selesai

Gelung acara

Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

Bulatan Gelung Acara

Bulatan kelabu dalam gambar berkaitan dengan sistem pengendalian dan bukan fokus bab ini. Fokus pada bulatan kuning dan oren dan kotak oren di tengah.

Kami memanggil setiap kitaran gelung peristiwa sebagai "gelung", juga dipanggil "polling", juga dipanggil "Tick".

Satu kitaran melalui enam peringkat:

  • pemasa : pemasa (fungsi panggil balik seperti setTimeout, setInterval, dll. disimpan di dalam)

  • panggil balik belum selesai

  • sediakan terbiar

  • polling: baris gilir pengundian (panggilan balik selain pemasa dan semakan disimpan di sini)

  • semak: Fasa semakan (panggilan balik menggunakan setImmediate akan memasuki baris gilir ini secara langsung)

  • tutup panggilan balik

Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

Kali ini kita hanya fokus pada tiga perkara utama yang ditandakan dengan warna merah di atas.

Cara ia berfungsi

  • Setiap peringkat mengekalkan baris gilir acara. Anda boleh menganggap setiap kalangan sebagai baris gilir acara.
  • Ini berbeza daripada pelayar Penyemak imbas mempunyai sehingga dua baris gilir (baris gilir makro dan gilir mikro). Tetapi terdapat enam baris gilir dalam nod
  • Selepas tiba di baris gilir, semak sama ada terdapat tugasan dalam baris gilir (iaitu, lihat jika terdapat fungsi panggil balik) yang perlu dilaksanakan. Jika ada, laksanakannya mengikut urutan sehingga semua pelaksanaan selesai dan baris gilir dibersihkan.
  • Jika tiada tugasan, masukkan baris gilir seterusnya untuk menyemak. Sehingga semua baris gilir disemak, ia dikira sebagai tinjauan pendapat.
  • Antaranya, timers, pending callback, idle prepare, dsb. tiba di baris gilir poll selepas pelaksanaan.

Cara baris gilir pemasa berfungsi

pemasa bukan baris gilir sebenar, ia disimpan secara dalaman Ia pemasa.
Setiap kali baris gilir ini dicapai, semua pemasa dalam urutan pemasa akan diperiksa Berbilang pemasa dalam urutan pemasa diisih mengikut kronologi.

Semak proses: Kira setiap pemasa dalam turutan dan hitung sama ada masa dari masa pemasa mula mengira ke masa semasa memenuhi tetapan parameter selang pemasa (contohnya, 1000ms, hitung apabila pemasa mula mengira ke Is ada 1m sekarang). Apabila semakan pemasa berlalu, fungsi panggil baliknya dilaksanakan.

Cara baris gilir tinjauan pendapat beroperasi

  • Jika terdapat fungsi panggil balik dalam tinjauan pendapat yang perlu dilaksanakan, panggilan balik dilaksanakan mengikut urutan , sehingga baris gilir dikosongkan.
  • Jika tiada fungsi panggil balik dalam tinjauan pendapat yang perlu dilaksanakan, baris gilir sudah kosong. Ia akan menunggu di sini untuk panggilan balik muncul dalam baris gilir lain
    • Jika panggilan balik muncul dalam baris gilir lain, ia akan pergi dari tinjauan pendapat ke bawah ke atas, menamatkan peringkat ini dan memasuki peringkat seterusnya.
    • Jika tiada panggilan balik dalam baris gilir lain, ia akan terus menunggu dalam baris gilir tinjauan pendapat sehingga panggilan balik berlaku dalam mana-mana baris gilir sebelum memulakan kerja. (Beginilah cara orang yang malas mengendalikan sesuatu)

Contoh untuk menyelesaikan proses acara

setTimeout(() => {
  console.log('object');
}, 5000)
console.log('node');

Aliran peristiwa kod di atas diisih

  • Masukkan urutan utama , laksanakan setTimeout(), dan fungsi panggil balik adalah tak segerak Tugas dimasukkan ke dalam baris gilir pemasa giliran tak segerak dan tidak akan dilaksanakan buat sementara waktu.
  • Teruskan ke bawah, laksanakan konsol di belakang pemasa dan cetak "nod".
  • Tentukan sama ada terdapat gelung peristiwa. Ya, lalui kitaran tinjauan pendapat: daripada pemasa - panggilan balik yang belum selesai - sediakan melahu...
  • ke tinjauan pendapat beratur untuk berhenti menggelung dan menunggu.
    • Memandangkan masa ini belum sampai 5 saat, baris gilir pemasa tidak mempunyai tugas, jadi ia telah tersekat dalam baris gilir tinjauan pendapat, dan pada masa yang sama ia membuat tinjauan untuk menyemak sama ada terdapat tugas dalam baris gilir lain .
  • Tunggu selama 5 saat untuk tiba Panggilan balik setTimeout dimasukkan ke dalam pemasa tinjauan rutin memeriksa bahawa terdapat tugas dalam baris gilir pemasa, dan kemudian turun dan mencapai pemasa selepas menyemak dan. menutup panggilan balik. Kosongkan baris gilir pemasa.
  • Teruskan tinjauan pendapat dan tunggu, bertanya sama ada gelung acara masih diperlukan. Jika tidak, ia akan sampai ke penghujungnya.

Untuk memahami masalah ini, lihat kod dan analisis proses di bawah:

setTimeout(function t1() {
  console.log('setTimeout');
}, 5000)
console.log('node 生命周期');

const http = require('http')

const server = http.createServer(function h1() {
  console.log('请求回调');
});

server.listen(8080)

Kod Analisis adalah seperti berikut:

  • Seperti biasa, laksanakan utas utama dahulu, cetak "kitaran hayat nod", perkenalkan http dan kemudian buat perkhidmatan http.
  • Kemudian gelung acara menyemak sama ada terdapat tugas tak segerak dan mendapati terdapat tugas pemasa dan tugasan permintaan. Jadi masukkan gelung acara.
  • Jika tiada tugasan dalam enam baris gilir, tunggu dalam baris gilir tinjauan pendapat. Seperti yang ditunjukkan di bawah:

    Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

  • Selepas lima saat, terdapat tugasan dalam pemasa, dan proses bermula dari tinjauan pendapat hingga keluar ke bawah, selepas melalui semak dan tutup baris gilir panggil balik , mencapai gelung acara.
  • Gelung acara menyemak sama ada terdapat tugasan tak segerak dan mendapati bahawa terdapat tugas pemasa dan meminta tugas. Jadi masukkan gelung acara sekali lagi.
  • Tiba dalam baris gilir pemasa dan mendapati terdapat tugas fungsi panggil balik, kemudian laksanakan panggilan balik dalam urutan, kosongkan baris gilir pemasa (sudah tentu hanya ada satu panggilan balik yang tiba selepas 5 saat, jadi hanya laksanakannya secara langsung), cetak " setTimeout". Seperti yang ditunjukkan di bawah

    Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

  • Selepas mengosongkan baris gilir pemasa, pengundian diteruskan ke bawah baris gilir tinjauan pendapat Memandangkan baris gilir undian kini menjadi baris gilir kosong, ia menunggu di sini.
  • Kemudian, dengan mengandaikan permintaan pengguna dihantar, fungsi panggil balik h1 diletakkan dalam baris gilir tinjauan pendapat. Jadi terdapat fungsi panggil balik dalam tinjauan pendapat yang perlu dilaksanakan dan panggilan balik dilaksanakan mengikut urutan sehingga baris gilir tinjauan pendapat dikosongkan.
  • Baris gilir tinjauan pendapat dikosongkan pada masa ini, baris gilir tinjauan pendapat kosong dan terus menunggu.

    Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

  • 由于node线程一直holding在poll队列,等很长一段时间还是没有任务来临时,会自动断开等待(不自信表现),向下执行轮询流程,经过check、close callbacks后到达event loop
  • 到了event loop后,检查是否有异步任务,检查发现有请求任务。(此时定时器任务已经执行完毕,所以没有了),则继续再次进入事件循环。
  • 到达poll队列,再次holding……
  • 再等很长时间没有任务来临,自动断开到even loop(再补充一点无任务的循环情况)
  • 再次回到poll队列挂起
  • 无限循环……

梳理事件循环流程图:

注意:下图中的“是否有任务”的说法表示“是否有本队列的任务”。

1Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

event loop流程梳理

再用一个典型的例子验证下流程:

const startTime = new Date();

setTimeout(function f1() {
  console.log('setTimeout', new Date(), new Date() - startTime);
}, 200)

console.log('node 生命周期', startTime);

const fs = require('fs')

fs.readFile('./poll.js', 'utf-8', function fsFunc(err, data) {
  const fsTime = new Date()
  console.log('fs', fsTime);
  while (new Date() - fsTime < 300) {
  }
  console.log(&#39;结束死循环&#39;, new Date());
});

连续运行三遍,打印结果如下:

1Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

执行流程解析:

  • 执行全局上下文,打印「node 生命周期 + 时间」

  • 询问是否有event loop

  • 有,进入timers队列,检查没有计时器(cpu处理速度可以,这时还没到200ms)

  • 轮询进入到poll,读文件还没读完(比如此时才用了20ms),因此poll队列是空的,也没有任务回调

  • 在poll队列等待……不断轮询看有没有回调

  • 文件读完,poll队列有了fsFunc回调函数,并且被执行,输出「fs + 时间」

  • 在while死循环那里卡300毫秒,

  • 死循环卡到200ms的时候,f1回调进入timers队列。但此时poll队列很忙,占用了线程,不会向下执行。

  • 直到300ms后poll队列清空,输出「结束死循环 + 时间」

  • event loop赶紧向下走

  • 再来一轮到timers,执行timers队列里的f1回调。于是看到「setTimeout + 时间」

  • timers队列清空,回到poll队列,没有任务,等待一会。

  • 等待时间够长后,向下回到event loop。

  • event loop检查没有其他异步任务了,结束线程,整个程序over退出。

check 阶段

检查阶段(使用 setImmediate 的回调会直接进入这个队列)

check队列的实际工作原理

真正的队列,里边扔的就是待执行的回调函数的集合。类似[fn,fn]这种形式的。
每次到达check这个队列后,立即按顺序执行回调函数即可【类似于[fn1,fn2].forEach((fn)=>fn())的感觉】

所以说,setImmediate不是一个计时器的概念。

如果你去面试,涉及到Node环节,可能会遇到下边这个问题:setImmediate和setTimeout(0)谁更快。

setImmediate() 与 setTimeout(0) 的对比

  • setImmediate的回调是异步的,和setTimeout回调性质一致。
  • setImmediate回调在check队列,setTimeout回调在timers队列(概念意义,实际在计时器线程,只是setTimeout在timers队列做检查调用而已。详细看timers的工作原理)。
  • setImmediate函数调用后,回调函数会立即push到check队列,并在下次eventloop时被执行。setTimeout函数调用后,计时器线程增加一个定时器任务,下次eventloop时会在timers阶段里检查判断定时器任务是否到达时间,到了则执行回调函数。
  • 综上,setImmediate的运算速度比setTimeout(0)的要快,因为setTimeout还需要开计时器线程,并增加计算的开销。

二者的效果差不多。但是执行顺序不定

观察以下代码:

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

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

多次反复运行,执行效果如下:

1Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

顺序不定

可以看到多次运行,两句console.log打印的顺序不定。
这是因为setTimeout的间隔数最小填1,虽然下边代码填了0。但实际计算机执行当1ms算。(这里注意和浏览器的计时器区分。在浏览器中,setInterval的最小间隔数为10ms,小于10ms则会被设置为10;设备供电状态下,间隔最小为16.6ms。)

以上代码,主线程运行的时候,setTimeout函数调用,计时器线程增加一个定时器任务。setImmediate函数调用后,其回调函数立即push到check队列。主线程执行完毕。

eventloop判断时,发现timers和check队列有内容,进入异步轮询:

第一种情况:等到了timers里这段时间,可能还没有1ms的时间,定时器任务间隔时间的条件不成立所以timers里还没有回调函数。继续向下到了check队列里,这时候setImmediate的回调函数早已等候多时,直接执行。而再下次eventloop到达timers队列,定时器也早已成熟,才会执行setTimeout的回调任务。于是顺序就是「setImmediate -> setTimeout」。

第二种情况:但也有可能到了timers阶段时,超过了1ms。于是计算定时器条件成立,setTimeout的回调函数被直接执行。eventloop再向下到达check队列执行setImmediate的回调。最终顺序就是「setTimeout -> setImmediate」了。

所以,只比较这两个函数的情况下,二者的执行顺序最终结果取决于当下计算机的运行环境以及运行速度。

二者时间差距的对比代码

------------------setTimeout测试:-------------------
let i = 0;
console.time(&#39;setTimeout&#39;);
function test() {
  if (i < 1000) {
    setTimeout(test, 0)
    i++
  } else {
    console.timeEnd(&#39;setTimeout&#39;);
  }
}
test();

------------------setImmediate测试:-------------------
let i = 0;
console.time(&#39;setImmediate&#39;);
function test() {
  if (i < 1000) {
    setImmediate(test)
    i++
  } else {
    console.timeEnd(&#39;setImmediate&#39;);
  }
}
test();

运行观察时间差距:

1Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

setTimeout与setImmediate时间差距

可见setTimeout远比setImmediate耗时多得多
这是因为setTimeout不仅有主代码执行的时间消耗。还有在timers队列里,对于计时器线程中各个定时任务的计算时间。

结合poll队列的面试题(考察timers、poll和check的执行顺序)

如果你看懂了上边的事件循环图,下边这道题难不倒你!

// 说说下边代码的执行顺序,先打印哪个?
const fs = require(&#39;fs&#39;)
fs.readFile(&#39;./poll.js&#39;, () => {
  setTimeout(() => console.log(&#39;setTimeout&#39;), 0)
  setImmediate(() => console.log(&#39;setImmediate&#39;))
})

上边这种代码逻辑,不管执行多少次,肯定都是先执行setImmediate。

1Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

先执行setImmediate

因为fs各个函数的回调是放在poll队列的。当程序holding在poll队列后,出现回调立即执行。
回调内执行setTimeout和setImmediate的函数后,check队列立即增加了回调。
回调执行完毕,轮询检查其他队列有内容,程序结束poll队列的holding向下执行。
check是poll阶段的紧接着的下一个。所以在向下的过程中,先执行check阶段内的回调,也就是先打印setImmediate。
到下一轮循环,到达timers队列,检查setTimeout计时器符合条件,则定时器回调被执行。

nextTick 与 Promise

说完宏任务,接下来说下微任务

  • 二者都是「微队列」,执行异步微任务。
  • 二者不是事件循环的一部分,程序也不会开启额外的线程去处理相关任务。(理解:promise里发网络请求,那是网络请求开的网络线程,跟Promise这个微任务没关系)
  • 微队列设立的目的就是让一些任务「马上」、「立即」优先执行。
  • nextTick与Promise比较,nextTick的级别更高。

nextTick表现形式

process.nextTick(() => {})

Promise表现形式

Promise.resolve().then(() => {})

如何参与事件循环?

事件循环中,每执行一个回调前,先按序清空一次nextTick和promise。

// 先思考下列代码的执行顺序
setImmediate(() => {
  console.log(&#39;setImmediate&#39;);
});

process.nextTick(() => {
  console.log(&#39;nextTick 1&#39;);
  process.nextTick(() => {
    console.log(&#39;nextTick 2&#39;);
  })
})

console.log(&#39;global&#39;);


Promise.resolve().then(() => {
  console.log(&#39;promise 1&#39;);
  process.nextTick(() => {
    console.log(&#39;nextTick in promise&#39;);
  })
})

最终顺序:

  • global

  • nextTick 1

  • nextTick 2

  • promise 1

  • nextTick in promise

  • setImmediate

两个问题:

基于上边的说法,有两个问题待思考和解决:

  • 每走一个异步宏任务队列就查一遍nextTick和promise?还是每执行完 宏任务队列里的一个回调函数就查一遍呢?

  • 如果在poll的holding阶段,插入一个nextTick或者Promise的回调,会立即停止poll队列的holding去执行回调吗?

Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

上边两个问题,看下边代码的说法

setTimeout(() => {
  console.log(&#39;setTimeout 100&#39;);
  setTimeout(() => {
    console.log(&#39;setTimeout 100 - 0&#39;);
    process.nextTick(() => {
      console.log(&#39;nextTick in setTimeout 100 - 0&#39;);
    })
  }, 0)
  setImmediate(() => {
    console.log(&#39;setImmediate in setTimeout 100&#39;);
    process.nextTick(() => {
      console.log(&#39;nextTick in setImmediate in setTimeout 100&#39;);
    })
  });
  process.nextTick(() => {
    console.log(&#39;nextTick in setTimeout100&#39;);
  })
  Promise.resolve().then(() => {
    console.log(&#39;promise in setTimeout100&#39;);
  })
}, 100)

const fs = require(&#39;fs&#39;)
fs.readFile(&#39;./1.poll.js&#39;, () => {
  console.log(&#39;poll 1&#39;);
  process.nextTick(() => {
    console.log(&#39;nextTick in poll ======&#39;);
  })
})

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

setTimeout(() => {
  console.log(&#39;setTimeout 1&#39;);
  Promise.resolve().then(() => {
    console.log(&#39;promise in setTimeout1&#39;);
  })
  process.nextTick(() => {
    console.log(&#39;nextTick in setTimeout1&#39;);
  })
}, 1)

setImmediate(() => {
  console.log(&#39;setImmediate&#39;);
  process.nextTick(() => {
    console.log(&#39;nextTick in setImmediate&#39;);
  })
});

process.nextTick(() => {
  console.log(&#39;nextTick 1&#39;);
  process.nextTick(() => {
    console.log(&#39;nextTick 2&#39;);
  })
})

console.log(&#39;global ------&#39;);

Promise.resolve().then(() => {
  console.log(&#39;promise 1&#39;);
  process.nextTick(() => {
    console.log(&#39;nextTick in promise&#39;);
  })
})

/** 执行顺序如下
global ------
nextTick 1
nextTick 2
promise 1
nextTick in promise
setTimeout 0 // 解释问题1. 没有上边的nextTick和promise,setTimeout和setImmediate的顺序不一定,有了以后肯定是0先开始。
// 可见,执行一个队列之前,就先检查并执行了nextTick和promise微队列
nextTick in setTimeout
setTimeout 1
nextTick in setTimeout1
promise in setTimeout1
setImmediate
nextTick in setImmediate
poll 1
nextTick in poll ======
setTimeout 100
nextTick in setTimeout100
promise in setTimeout100
setImmediate in setTimeout 100
nextTick in setImmediate in setTimeout 100
setTimeout 100 - 0
nextTick in setTimeout 100 - 0
 */

以上代码执行多次,顺序不变,setTimeout和setImmediate的顺序都没变。

执行顺序及具体原因说明如下:

  • global :主线程同步任务,率先执行没毛病

  • nextTick 1:执行异步宏任务之前,清空异步微任务,nextTick优先级高,先行一步

  • nextTick 2:执行完上边这句代码,又一个nextTick微任务,立即率先执行

  • promise 1:执行异步宏任务之前,清空异步微任务,Promise的优先级低,所以在nextTick完了以后立即执行

  • nextTick in promise:清空Promise队列的过程中,遇到nextTick微任务,立即执行、清空

  • setTimeout 0: 解释第一个问题. 没有上边的nextTick和promise,只有setTimeout和setImmediate时他俩的执行顺序不一定。有了以后肯定是0先开始。可见,执行一个宏队列之前,就先按顺序检查并执行了nextTick和promise微队列。等微队列全部执行完毕,setTimeout(0)的时机也成熟了,就被执行。

  • nextTick in setTimeout:执行完上边这句代码,又一个nextTick微任务,立即率先执行 【这种回调函数里的微任务,我不能确定是紧随同步任务执行的;还是放到微任务队列,等下一个宏任务执行前再清空的他们。但是顺序看上去和立即执行他们一样。不过我比较倾向于是后者:先放到微任务队列等待,下一个宏任务执行前清空他们。】

  • setTimeout 1:因为执行微任务耗费时间,导致此时timers里判断两个0和1的setTimeout计时器已经结束,所以两个setTimeout回调都已加入队列并被执行

  • nextTick in setTimeout1:执行完上边这句代码,又一个nextTick微任务,立即率先执行 【可能是下一个宏任务前清空微任务】

  • promise in setTimeout1:执行完上边这句代码,又一个Promise微任务,立即紧随执行 【可能是下一个宏任务前清空微任务】

  • setImmediate:poll队列回调时机未到,先行向下到check队列,清空队列,立即执行setImmediate回调

  • nextTick in setImmediate:执行完上边这句代码,又一个nextTick微任务,立即率先执行 【可能是下一个宏任务前清空微任务】

  • poll 1:poll队列实际成熟,回调触发,同步任务执行。

  • nextTick in poll :执行完上边这句代码,又一个nextTick微任务,立即率先执行 【可能是下一个宏任务前清空微任务】

  • setTimeout 100:定时器任务到达时间,执行回调。并在回调里往微任务推入了nextTick、Promise,往宏任务的check里推入了setImmediate的回调。并且也开启了计时器线程,往timers里增加了下一轮回调的可能。

  • nextTick in setTimeout100:宏任务向下前,率先执行定时器回调内新增的微任务-nextTick 【这里就能确定了,是下一个宏任务前清空微任务的流程】

  • promise in setTimeout100:紧接着执行定时器回调内新增的微任务-Promise 【清空完nextTick清空Promise的顺序】

  • setImmediate in setTimeout 100:这次setImmediate比setTimeout(0)先执行的原因是:流程从timers向后走到check队列,已经有了setImmediate的回调,立即执行。

  • nextTick in setImmediate in setTimeout 100:执行完上边这句代码,又一个nextTick微任务,下一个宏任务前率先清空微任务

  • setTimeout 100 - 0:轮询又一次回到timers,执行100-0的回调。

  • nextTick in setTimeout 100 - 0:执行完上边这句代码,又一个nextTick微任务,下一个宏任务前率先清空微任务。

扩展:为什么有了setImmediate还要有nextTick和Promise?

一开始设计的时候,setImmediate充当了微队列的作用(虽然他不是)。设计者希望执行完poll后立即执行setImmediate(当然现在也确实是这么表现的)。所以起的名字叫Immediate,表示立即的意思。 但是后来问题是,poll里可能有N个任务连续执行,在执行期间想要执行setImmediate是不可能的。因为poll队列不停,流程不向下执行。

于是出现nextTick,真正的微队列概念。但此时,immediate的名字被占用了,所以名字叫nextTick(下一瞬间)。事件循环期间,执行任何一个队列之前,都要检查他是否被清空。其次是Promise。

面试题

最后,检验学习成果的面试题来了

async function async1() {
  console.log(&#39;async start&#39;);
  await async2();
  console.log(&#39;async end&#39;);
}

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

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

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

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

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

async1();

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

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

// 答案如下
// -
// -
// -
// -
// -
// -
// -
// -
// -
// -
// -
// -






/**
script start
async start
async2
promise1
promise2
script end

nextTick
async end
promise 3

// 后边这仨的运行顺序就是验证你电脑运算速度的时候了。
速度最好(执行上边的同步代码 + 微任务 + 计时器运算用了不到0ms):
setImmediate
setTimeout 0
setTimeout 3

速度中等(执行上边的同步代码 + 微任务 + 计时器运算用了0~3ms以上):
setTimeout 0
setImmediate
setTimeout 3

速度较差(执行上边的同步代码 + 微任务 + 计时器运算用了3ms以上):
setTimeout 0
setTimeout 3
setImmediate
*/

思维脑图 - Node生命周期核心阶段

1Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs

更多编程相关知识,请访问:编程视频!!

Atas ialah kandungan terperinci Gabungan gambar dan teks akan membantu anda memahami gelung acara dalam Nodejs. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:juejin.cn. Jika ada pelanggaran, sila hubungi admin@php.cn Padam