ホームページ > 記事 > ウェブフロントエンド > Node.js のタイマーを理解する
関連する推奨事項: 「node js チュートリアル 」
タイマーは、ノード内の特定の時点で呼び出される関数をスケジュールするために使用されます。今後、Node .js のタイマー関数は、Web ブラウザーが提供するタイマー API に似た API を実装しますが、イベント ループを使用して実装します。Node.js
setTimeout(コールバック, 遅延[, ...args])
Node .js では、コールバックが呼び出される正確な時刻や順序は保証されず、コールバックは指定された時刻にできるだけ近い時刻に呼び出されます。 setTimeout 遅延が 2147483647 より大きいか 1 未満の場合、遅延は 1 に設定され、整数以外の遅延は整数に切り捨てられます。
実行順序がおかしいです。
setImmediate(console.log, 1); setTimeout(console.log, 1, 2); Promise.resolve(3).then(console.log); process.nextTick(console.log, 4); console.log(5);
は 5 4 3 2 1 または 5 4 3 1 2
同期 & 非同期
setImmediate(console.log, 1); setTimeout(console.log, 1, 2); Promise.resolve(3).then(console.log); process.nextTick(console.log, 4); /****************** 同步任务和异步任务的分割线 ********************/ console.log(5);
なので、最初に 5 を出力します。これは理解しやすいです。残りは非同期操作です。Node.js はどのような順序で実行されますか?
イベント ループ
┌───────────────────────────┐ ┌─>│ timers │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ pending callbacks │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ idle, prepare │ │ └─────────────┬─────────────┘ ┌───────────────┐ │ ┌─────────────┴─────────────┐ │ incoming: │ │ │ poll │<─────┤ connections, │ │ └─────────────┬─────────────┘ │ data, etc. │ │ ┌─────────────┴─────────────┐ └───────────────┘ │ │ check │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ └──┤ close callbacks │ └───────────────────────────┘
イベントループの各ステージにはタスクキューがあり、イベントループが特定のステージに入ると、そのステージのタスクキューはは、キューがクリアされるか、実行されたコールバックがシステムの上限に達するまで、次のステージに移動しません。すべてのステージが順番に実行されると、イベント ループはティックを完了したと言われます
非同期操作は次に配置されます。イベント ループ ティックでは、process.nextTick は次のイベント ループ ティックに入る前に実行されるため、他の非同期操作の前にある必要があります
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);
各ステージの主なタスク
: setTimeout、setInterval コールバックを実行します。
: I/O を実行します (ファイル、ネットワークなど) .) callbacks
: システムによって内部的にのみ呼び出されます
: 新しい I/O イベントを取得し、関連するコールバックを実行し、適切な条件下でノードをブロックします。
: この段階で setImmediate コールバックが実行されます
: ソケットなどの close イベント コールバックを実行します。
timers
フェーズで呼び出されます。タイムアウト タイマーは、設定時間が経過した後、できるだけ早くコールバックを呼び出します。ただし、 印刷の順序が異なることがわかります。 I/Oをブロックしてポーリングする必要があります その後、ポーリングキュー内のイベントを処理します イベントループ時ポーリングフェーズに入ったときスケジュールされたタイマーではありません この段階で setImmediate コールバックを実行します。 前端同学肯定都听说过 micoTask 和 macroTask,Promise.then 属于 microTask,在浏览器环境下 microTask 任务会在每个 macroTask 执行最末端调用 setImmediate 听起来是立即执行,process.nextTick 听起来是下一个时钟执行,为什么效果是反过来的?这就要从那段不堪回首的历史讲起 最开始的时候只有 process.nextTick 方法,没有 setImmediate 方法,通过上面的分析可以看出来任何时候调用 process.nextTick(),nextTick 会在 event loop 之前执行,直到 nextTick 队列被清空才会进入到下一 event loop,如果出现 process.nextTick 的递归调用程序没有被正确结束,那么 IO 的回调将没有机会被执行 执行程序将返回 于是乎需要一个不这么 bug 的调用,setImmediate 方法出现了,比较令人费解的是在 process.nextTick 起错名字的情况下,setImmediate 也用了一个错误的名字以示区分。。。 那么是不是编程中应该杜绝使用 process.nextTick 呢?官方推荐大部分时候应该使用 setImmediate,同时对 process.nextTick 的最大调用堆栈做了限制,但 process.nextTick 的调用机制确实也能为我们解决一些棘手的问题 允许用户在 even tloop 开始之前 处理异常、执行清理任务 允许回调在调用栈 unwind 之后,下次 event loop 开始之前执行 一个类继承了 EventEmitter,而且想在实例化的时候触发一个事件 在构造函数执行 更多编程相关知识,请访问:编程教学!! 以上がNode.js のタイマーを理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
複数回実行された場合
#投票ステージには主に 2 つのタスクがあります
ポーリング キューが空でない場合、イベント ループはコールバック キューを循環し、キューが使い果たされるか、システムに到達するか、コールバックの最大数に達するまで同期的に実行されますポーリング キューが空の場合#setImmediate() タスクがある場合、イベント ループは
フェーズに入ります。フェーズ
キューが空になると、イベント ループはタイマー キューが空かどうかを確認します。空でない場合は、次のラウンドのイベントに入ります。前述のとおり、異なる I/O の場合、 setTimeout と setImmediate の実行順序は決定できませんが、 setTimeout と setImmediate が I/O コールバック内にある場合は、 setImmediate が最初に実行される必要があります。 setImmediate() タスクはポーリング フェーズで検出され、イベント ループは直接チェック フェーズに入り、setImmediate コールバックを実行します。 const fs = require('fs');
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
});
check为什么 Promise.then 比 setTimeout 早一些
在 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
const fs = require('fs');
fs.readFile('a.txt', (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!
const EventEmitter = require('events');
const util = require('util');
function MyEmitter() {
EventEmitter.call(this);
this.emit('event');
}
util.inherits(MyEmitter, EventEmitter);
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
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!');
});