Rumah > Tutorial CMS > WordTekan > Memahami gelung acara dalam JavaScript

Memahami gelung acara dalam JavaScript

PHPz
Lepaskan: 2023-09-03 13:17:10
asal
1091 orang telah melayarinya

Memahami gelung acara dalam JavaScript

Anda mungkin sudah tahu bahawa JavaScript ialah bahasa pengaturcaraan satu benang. Ini bermakna JavaScript berjalan pada satu urutan utama dalam penyemak imbas web atau Node.js. Berjalan pada satu utas utama bermakna menjalankan hanya satu kod JavaScript pada satu masa.

Gelung peristiwa dalam JavaScript memainkan peranan penting dalam menentukan cara kod dilaksanakan pada urutan utama. Gelung acara bertanggungjawab untuk perkara seperti pelaksanaan kod dan pengumpulan dan pemprosesan acara. Ia juga mengendalikan pelaksanaan mana-mana subtugas beratur.

Dalam tutorial ini, anda akan mempelajari asas gelung acara dalam JavaScript.

Cara gelung acara berfungsi

Untuk memahami cara gelung acara berfungsi, anda perlu memahami tiga istilah penting.

Timbunan

Timbunan panggilan hanyalah timbunan panggilan fungsi yang mengesan konteks pelaksanaan fungsi. Timbunan mengikut prinsip masuk-dahulu-keluar (LIFO), yang bermaksud bahawa fungsi yang paling baru dipanggil akan menjadi yang pertama dilaksanakan.

Beratur

Baris gilir mengandungi satu siri tugasan yang dilaksanakan oleh JavaScript. Tugasan dalam baris gilir ini boleh menyebabkan fungsi dipanggil, yang kemudiannya diletakkan pada timbunan. Pemprosesan baris gilir bermula hanya apabila timbunan kosong. Item dalam baris gilir mengikut prinsip masuk dahulu, keluar dahulu (FIFO). Ini bermakna tugas yang paling lama akan diselesaikan terlebih dahulu.

Timbunan

Heap pada asasnya adalah kawasan memori yang besar di mana objek disimpan dan diperuntukkan. Tujuan utamanya adalah untuk menyimpan data yang mungkin digunakan oleh fungsi pada timbunan.

Pada asasnya, JavaScript adalah satu-benang dan melaksanakan satu fungsi pada satu masa. Fungsi tunggal ini diletakkan pada tindanan. Fungsi ini juga boleh mengandungi fungsi bersarang lain, yang akan diletakkan lebih tinggi dalam tindanan. Tindanan mengikut prinsip LIFO, jadi fungsi bersarang yang paling baru dipanggil akan dilaksanakan terlebih dahulu.

Tugas tak segerak seperti permintaan API atau pemasa akan ditambahkan pada gilir untuk pelaksanaan kemudian. Enjin JavaScript mula melaksanakan tugas dalam baris gilir apabila ia melahu.

Pertimbangkan contoh berikut:

function helloWorld() {
    console.log("Hello, World!");
}

function helloPerson(name) {
    console.log(`Hello, ${name}!`);
}

function helloTeam() {
    console.log("Hello, Team!");
    helloPerson("Monty");
}

function byeWorld() {
    console.log("Bye, World!");
}

helloWorld();
helloTeam();
byeWorld();

/* Outputs:

Hello, World!
Hello, Team!
Hello, Monty!
Bye, World!

*/
Salin selepas log masuk

Mari kita lihat rupa susunan dan baris gilir jika kita menjalankan kod di atas.

Panggil fungsi helloWorld() dan letakkan pada timbunan. Ia mencatatkan bahawa helloWorld() 函数并将其放入堆栈中。它记录 Hello, World! 完成其执行,因此它被从堆栈中取出。接下来调用 helloTeam() 函数并将其放入堆栈中。在执行过程中,我们记录 Hello, Team! 并调用 helloPerson()helloTeam() 的执行还没有完成,所以它停留在堆栈上,而 helloPerson() 则放在它上面。

后进先出原则规定 helloPerson() 现在执行。这会将 Hello, Monty! 记录到控制台,从而完成其执行,并且 helloPerson() 将从堆栈中取出。之后 helloTeam() 函数就会出栈,我们最终到达 byeWorld()。它会记录再见,世界!,然后从堆栈中消失。

队列一直是空的。

现在,考虑上述代码的细微变化:

function helloWorld() {
    console.log("Hello, World!");
}

function helloPerson(name) {
    console.log(`Hello, ${name}!`);
}

function helloTeam() {
    console.log("Hello, Team!");
    setTimeout(() => {
        helloPerson("Monty");
    }, 0);
}

function byeWorld() {
    console.log("Bye, World!");
}

helloWorld();
helloTeam();
byeWorld();

/* Outputs:

Hello, World!
Hello, Team!
Bye, World!
Hello, Monty!

*/
Salin selepas log masuk

我们在这里所做的唯一更改是使用 setTimeout()。但是,超时已设置为零。因此,我们期望 Hello, Monty!Bye, World! 之前输出。如果您了解事件循环的工作原理,您就会明白为什么不会发生这种情况。< /p>

helloTeam()入栈时,遇到setTimeout()方法。但是,setTimeout() 中对 helloPerson() 的调用会被放入队列中,一旦没有同步任务需要执行,就会被执行。

一旦对 byeWorld() 的调用完成,事件循环将检查队列中是否有任何挂起的任务,并找到对 helloPerson() 的调用。此时,它执行该函数并将 Hello, Monty! 记录到控制台。

这表明您提供给 setTimeout() 的超时持续时间并不是回调执行的保证时间。这是执行回调的最短时间。

保持我们的网页响应

JavaScript 的一个有趣的特性是它会运行一个函数直到完成。这意味着只要函数在堆栈上,事件循环就无法处理队列中的任何其他任务或执行其他函数。

这可能会导致网页“挂起”,因为它无法执行其他操作,例如处理用户输入或进行与 DOM 相关的更改。考虑以下示例,我们在其中查找给定范围内的素数数量:

function isPrime(num) {
  if (num <= 1) {
    return false;
  }

  for (let i = 2; i <= Math.sqrt(num); i++) {
    if (num % i === 0) {
      return false;
    }
  }
  
  return true;
}

function listPrimesInRange(start, end) {
  const primes = [];

  for (let num = start; num <= end; num++) {
    if (isPrime(num)) {
      primes.push(num);
    }
  }

  return primes;
}
Salin selepas log masuk

在我们的 listPrimesInRange() 函数中,我们迭代从 startend 的数字。对于每个数字,我们调用 isPrime() 函数来查看它是否是素数。 isPrime() 函数本身有一个 for 循环,该循环从 2Math.sqrt(num)Hello, World!

telah menyelesaikan pelaksanaannya, jadi ia telah dikeluarkan dari timbunan. Seterusnya fungsi helloTeam() dipanggil dan diletakkan pada timbunan. Semasa pelaksanaan, kami log 🎜Hello, Team!🎜 dan memanggil helloPerson(). Pelaksanaan helloTeam() belum selesai lagi, jadi ia kekal pada tindanan dan helloPerson() diletakkan di atasnya. 🎜 🎜Prinsip last-in-first-out menetapkan bahawa helloPerson() dilaksanakan sekarang. Ini akan log 🎜Hello, Monty!🎜 ke konsol, sekali gus melengkapkan pelaksanaannya dan helloPerson() akan muncul dari timbunan. Kemudian fungsi helloTeam() akan muncul dari timbunan, dan akhirnya kami mencapai byeWorld(). Ia akan merakam 🎜Selamat tinggal, dunia! 🎜 kemudian hilang dari timbunan. 🎜 🎜Barisan sentiasa kosong. 🎜 🎜Sekarang, pertimbangkan sedikit variasi kod di atas: 🎜
function listPrimesInRangeResponsively(start) {
  let next = start + 100,000;

  if (next > end) {
    next = end;
  }

  for (let num = start; num <= next; num++) {
    if (isPrime(num)) {
      primeNumbers.push(num);
    }

    if (num == next) {
      percentage = ((num - begin) * 100) / (end - begin);
      percentage = Math.floor(percentage);

      progress.innerText = `Progress ${percentage}%`;

      if (num != end) {
        setTimeout(() => {
          listPrimesInRangeResponsively(next + 1);
        });
      }
    }

    if (num == end) {
      percentage = ((num - begin) * 100) / (end - begin);
      percentage = Math.floor(percentage);

      progress.innerText = `Progress ${percentage}%`;

      heading.innerText = `${primeNumbers.length - 1} Primes Found!`;

      console.log(primeNumbers);

      return primeNumbers;
    }
  }
}
Salin selepas log masuk
Salin selepas log masuk
🎜Satu-satunya perubahan yang kami buat di sini ialah menggunakan setTimeout(). Walau bagaimanapun, tamat masa telah ditetapkan kepada sifar. Oleh itu, kami menjangkakan 🎜Hello, Monty!🎜 akan dikeluarkan sebelum 🎜Bye, World!🎜. Jika anda memahami cara gelung acara berfungsi, anda akan faham mengapa ini tidak berlaku.

🎜Apabila helloTeam() ditolak ke tindanan, kaedah setTimeout() ditemui. Walau bagaimanapun, panggilan ke helloPerson() dalam setTimeout() akan dimasukkan ke dalam baris gilir dan akan dilaksanakan sebaik sahaja tiada tugas penyegerakan untuk dilaksanakan. 🎜 🎜Setelah panggilan ke byeWorld() selesai, gelung acara menyemak baris gilir untuk sebarang tugas yang belum selesai dan mencari panggilan ke helloPerson(). Pada ketika ini, ia melaksanakan fungsi dan log 🎜Hello, Monty!🎜 ke konsol. 🎜 🎜Ini menunjukkan bahawa tempoh tamat masa yang anda berikan kepada setTimeout() bukanlah masa yang dijamin untuk panggilan balik dilaksanakan. Ini ialah masa minimum untuk panggilan balik dilaksanakan. 🎜

Memastikan halaman web kami responsif 🎜 🎜Ciri JavaScript yang menarik ialah ia akan menjalankan fungsi sehingga ia selesai. Ini bermakna selagi fungsi berada pada timbunan, gelung peristiwa tidak boleh memproses sebarang tugas lain dalam baris gilir atau melaksanakan fungsi lain. 🎜 🎜Ini boleh menyebabkan halaman web "tergantung" kerana ia tidak dapat melakukan operasi lain, seperti memproses input pengguna atau membuat perubahan berkaitan DOM. Pertimbangkan contoh berikut, di mana kita dapati bilangan nombor perdana dalam julat tertentu: 🎜 rrreee 🎜Dalam fungsi listPrimesInRange() kami, kami mengulangi nombor daripada start hingga end. Untuk setiap nombor, kami memanggil fungsi isPrime() untuk melihat sama ada ia adalah prima. Fungsi isPrime() itu sendiri mempunyai gelung for yang pergi dari 2 ke Math.sqrt(num) Tentukan sama ada nombor adalah perdana. 🎜

查找给定范围内的所有素数可能需要一段时间,具体取决于您使用的值。当浏览器进行此计算时,它无法执行任何其他操作。这是因为 listPrimesInRange() 函数将保留在堆栈中,浏览器将无法执行队列中的任何其他任务。

现在,看一下以下函数:

function listPrimesInRangeResponsively(start) {
  let next = start + 100,000;

  if (next > end) {
    next = end;
  }

  for (let num = start; num <= next; num++) {
    if (isPrime(num)) {
      primeNumbers.push(num);
    }

    if (num == next) {
      percentage = ((num - begin) * 100) / (end - begin);
      percentage = Math.floor(percentage);

      progress.innerText = `Progress ${percentage}%`;

      if (num != end) {
        setTimeout(() => {
          listPrimesInRangeResponsively(next + 1);
        });
      }
    }

    if (num == end) {
      percentage = ((num - begin) * 100) / (end - begin);
      percentage = Math.floor(percentage);

      progress.innerText = `Progress ${percentage}%`;

      heading.innerText = `${primeNumbers.length - 1} Primes Found!`;

      console.log(primeNumbers);

      return primeNumbers;
    }
  }
}
Salin selepas log masuk
Salin selepas log masuk

这一次,我们的函数仅在批量处理范围时尝试查找素数。它通过遍历所有数字但一次仅处理其中的 100,000 个来实现这一点。之后,它使用 setTimeout() 触发对同一函数的下一次调用。

setTimeout() 被调用而没有指定延迟时,它会立即将回调函数添加到事件队列中。

下一个调用将被放入队列中,暂时清空堆栈以处理任何其他任务。之后,JavaScript 引擎开始在下一批 100,000 个数字中查找素数。

尝试单击此页面上的计算(卡住)按钮,您可能会收到一条消息,指出该网页正在减慢您的浏览器速度,并建议您停止该脚本。 p>

另一方面,单击计算(响应式)按钮仍将使网页保持响应式。

最终想法

在本教程中,我们了解了 JavaScript 中的事件循环以及它如何有效地执行同步和异步代码。事件循环使用队列来跟踪它必须执行的任务。

由于 JavaScript 不断执行函数直至完成,因此进行大量计算有时会“挂起”浏览器窗口。根据我们对事件循环的理解,我们可以重写我们的函数,以便它们批量进行计算。这允许浏览器保持窗口对用户的响应。它还使我们能够定期向用户更新我们在计算中取得的进展。

Atas ialah kandungan terperinci Memahami gelung acara dalam JavaScript. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan