Heim  >  Artikel  >  Web-Frontend  >  Timer in Node.js verstehen

Timer in Node.js verstehen

青灯夜游
青灯夜游nach vorne
2020-12-03 18:11:133152Durchsuche

Timer in Node.js verstehen

Verwandte Empfehlungen: „node js-Tutorial

Timer wird verwendet, um Funktionen zu planen, die zu einem bestimmten Zeitpunkt in der Zukunft aufgerufen werden sollen. Die Timer-Funktion in Node.js implementiert die von der bereitgestellte Timer-API Webbrowser Ähnliche API, aber mit Ereignisschleifenimplementierung gibt es vier verwandte Methoden in Node.js

  • setTimeout(callback, delay[, ...args])

  • setInterval(callback[, ... args ])

  • setImmediate(callback[, ...args])

  • process.nextTick(callback[, ...args])

Die ersten beiden Bedeutungen stimmen mit denen im Web überein, Die letzten beiden gelten nur für Node.js. Der Effekt scheint setTimeout(callback, 0) zu sein, was am häufigsten in der Node.js-Programmierung verwendet wird

Node.js garantiert nicht den genauen Zeitpunkt, zu dem der Rückruf ausgelöst wird, und tut dies auch nicht Es garantiert ihnen die Reihenfolge, die Rückrufe werden so nah wie möglich an der angegebenen Zeit aufgerufen. setTimeout Wenn die Verzögerung größer als 2147483647 oder kleiner als 1 ist, wird die Verzögerung auf 1 gesetzt und eine nicht ganzzahlige Verzögerung wird auf eine Ganzzahl gekürzt Zahl

setImmediate(console.log, 1);
setTimeout(console.log, 1, 2);
Promise.resolve(3).then(console.log);
process.nextTick(console.log, 4);
console.log(5);
gibt 5 4 3 2 1 oder 5 4 3 1 2 aus

Synchron und Asynchron

Die fünfte Zeile ist synchrone Ausführung, die anderen sind asynchron

setImmediate(console.log, 1);
setTimeout(console.log, 1, 2);
Promise.resolve(3).then(console.log);
process.nextTick(console.log, 4);
/****************** 同步任务和异步任务的分割线 ********************/
console.log(5);
Also zuerst 5 drucken, das ist leicht zu verstehen, und der Rest sind alle asynchrone Vorgänge. In welcher Reihenfolge führt Node.js sie aus?

Ereignisschleife

Node.js initialisiert die Ereignisabfrage nach dem Start. Während des Prozesses verarbeitet es möglicherweise asynchrone Aufrufe, Timerplanung und process.nextTick() und beginnt dann mit der Verarbeitung der Ereignisschleife. Auf der offiziellen Website gibt es ein solches Bild, um die Operationssequenz der Ereignisschleife vorzustellen.

┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘

Jede Phase der Ereignisschleife verfügt über eine Aufgabenwarteschlange Warteschlange wird gelöscht. Wenn der ausgeführte Rückruf die Obergrenze des Systems erreicht, wird er zur nächsten Stufe verschoben. Wenn alle Stufen nacheinander ausgeführt werden, wird davon ausgegangen, dass die Ereignisschleife einen Tick abgeschlossen hat. Asynchrone Vorgänge werden in die nächste Stufe eingefügt Ereignisschleifen-Tick, Prozess. nextTick wird vor dem Eintritt in den nächsten Ereignisschleifen-Tick ausgeführt, daher muss er vor anderen asynchronen Vorgängen erfolgen

ausstehende Rückrufe

: I/O-Rückruf (Datei, Netzwerk usw.) ausführen

inaktiv, vorbereiten

: nur intern vom System aufgerufen

  • Abfrage: Neue I/O-Ereignisse abrufen, ausführen verwandte Rückrufe und blockieren Sie den Knoten unter geeigneten Bedingungen Asynchrone Aufgaben in der täglichen Entwicklung befinden sich in Timern, Abfragen und Überprüfungen.

  • Timer
  • Node.js prüft, ob in der Timer-Phase ein abgelaufener Timer vorhanden ist. Wenn vorhanden, wird der Rückruf in den Timer eingefügt Warteschlange, die auf die Ausführung wartet. Node.js verwendet einen einzelnen Thread und ist durch die Inaktivität des Hauptthreads begrenzt. Abhängig von der Situation und anderen Prozessen auf der Maschine gibt es keine Garantie dafür, dass der Timer zum genauen Zeitpunkt ausgeführt wird . Es gibt zwei Haupttypen von Timern:

  • Unmittelbar

    Timeout-Timeout
  • Sofortige Timer-Rückrufe werden in der check-Phase aufgerufen, der Timeout-Timer ruft den Rückruf so schnell wie möglich nach dem auf Die festgelegte Zeit läuft ab, aber wenn Sie sie mehrmals ausführen, werden Sie feststellen, dass die Reihenfolge des Druckens unterschiedlich ist. Die Zeit des Blockierens und des Abfragens von E/A Die Ereignisschleife tritt in die Abfragephase ein und es gibt keinen geplanten Timer von Rückrufen

  • Wenn die Umfragewarteschlange leer ist
  • Wenn eine setImmediate()-Aufgabe vorhanden ist, endet die Ereignisschleife in der Umfragephase. Nach dem Eintritt in die check-Phase

    Wenn keine setImmediate()-Aufgabe vorhanden ist, wird die Die Ereignisschleife blockiert in der Phase „Poll“ und wartet darauf, dass der Rückruf zur Warteschlange hinzugefügt wird, und führt ihn dann sofort aus . Wenn nicht, treten Sie in die nächste Runde der Ereignisschleife ein
  • Wie oben erwähnt, kann die Ausführungsreihenfolge von setTimeout und setImmediate nicht bestimmt werden, wenn sich setTimeout und setImmediate jedoch in einem E/A befinden Rückruf, setImmediate muss zuerst ausgeführt werden, da die setImmediate()-Aufgabe während der Abfragephase erkannt wird und die Ereignisschleife direkt in die Prüfphase eintritt, um den setImmediate-Rückruf auszuführen

    setImmediate(console.log, 1);
    setTimeout(console.log, 1, 2);
    Promise.resolve(3).then(console.log);
    /****************** 下次 event loop tick 分割线 ********************/
    process.nextTick(console.log, 4);
    /****************** 同步任务和异步任务的分割线 ********************/
    console.log(5);
    check

    Der setImmediate-Rückruf wird in dieser Phase ausgeführt
  • 为什么 Promise.then 比 setTimeout 早一些

    前端同学肯定都听说过 micoTask 和 macroTask,Promise.then 属于 microTask,在浏览器环境下 microTask 任务会在每个 macroTask 执行最末端调用

    在 Node.js 环境下 microTask 会在每个阶段完成之间调用,也就是每个阶段执行最后都会执行一下 microTask 队列

    setImmediate(console.log, 1);
    setTimeout(console.log, 1, 2);
    /****************** microTask 分割线 ********************/
    Promise.resolve(3).then(console.log); // microTask 分割线
    /****************** 下次 event loop tick 分割线 ********************/
    process.nextTick(console.log, 4);
    /****************** 同步任务和异步任务的分割线 ********************/
    console.log(5);

    setImmediate VS process.nextTick

    setImmediate 听起来是立即执行,process.nextTick 听起来是下一个时钟执行,为什么效果是反过来的?这就要从那段不堪回首的历史讲起

    最开始的时候只有 process.nextTick 方法,没有 setImmediate 方法,通过上面的分析可以看出来任何时候调用 process.nextTick(),nextTick 会在 event loop 之前执行,直到 nextTick 队列被清空才会进入到下一 event loop,如果出现 process.nextTick 的递归调用程序没有被正确结束,那么 IO 的回调将没有机会被执行

    const fs = require(&#39;fs&#39;);
    
    fs.readFile(&#39;a.txt&#39;, (err, data) => {
    	console.log('read file task done!');
    });
    
    let i = 0;
    function test(){
    	if(i++ < 999999) {
      	console.log(`process.nextTick ${i}`);
        process.nextTick(test);
      }
    }
    test();

    执行程序将返回

    nextTick 1
    nextTick 2
    ...
    ...
    nextTick 999999
    read file task done!

    于是乎需要一个不这么 bug 的调用,setImmediate 方法出现了,比较令人费解的是在 process.nextTick 起错名字的情况下,setImmediate 也用了一个错误的名字以示区分。。。

    那么是不是编程中应该杜绝使用 process.nextTick 呢?官方推荐大部分时候应该使用 setImmediate,同时对 process.nextTick 的最大调用堆栈做了限制,但 process.nextTick 的调用机制确实也能为我们解决一些棘手的问题

    • 允许用户在 even tloop 开始之前 处理异常、执行清理任务

    • 允许回调在调用栈 unwind 之后,下次 event loop 开始之前执行

    一个类继承了 EventEmitter,而且想在实例化的时候触发一个事件

    const EventEmitter = require(&#39;events&#39;);
    const util = require(&#39;util&#39;);
    
    function MyEmitter() {
      EventEmitter.call(this);
      this.emit(&#39;event&#39;);
    }
    util.inherits(MyEmitter, EventEmitter);
    
    const myEmitter = new MyEmitter();
    myEmitter.on(&#39;event&#39;, () => {
      console.log('an event occurred!');
    });

    在构造函数执行 this.emit('event') 会导致事件触发比事件回调函数绑定早,使用 process.nextTick 可以轻松实现预期效果

    const EventEmitter = require('events');
    const util = require('util');
    
    function MyEmitter() {
      EventEmitter.call(this);
    
      // use nextTick to emit the event once a handler is assigned
      process.nextTick(() => {
        this.emit('event');
      });
    }
    util.inherits(MyEmitter, EventEmitter);
    
    const myEmitter = new MyEmitter();
    myEmitter.on('event', () => {
      console.log('an event occurred!');
    });

    更多编程相关知识,请访问:编程教学!!

    Das obige ist der detaillierte Inhalt vonTimer in Node.js verstehen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:cnblogs.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen