nextTick vs setIm ngay lập tức, giải thích trực quan


90

Tôi rất bối rối về sự khác biệt giữa nextTick và setIm Instant. Tôi đã đọc tất cả các tài liệu về chúng trên internet nhưng tôi vẫn không hiểu chúng hoạt động như thế nào.

Ví dụ:

function log(n) { console.log(n); }

setIm Instant

setImmediate(function() {
  setImmediate(function() {
    log(1);
    setImmediate(function() { log(2); });
    setImmediate(function() { log(3); });
  });
  setImmediate(function() {
    log(4);
    setImmediate(function() { log(5); });
    setImmediate(function() { log(6); });
  });
});

//1 2 3 4 5 6

nextTick

process.nextTick(function() {
  process.nextTick(function() {
    log(1);
    process.nextTick(function() { log(2); });
    process.nextTick(function() { log(3); });
  });
  process.nextTick(function() {
    log(4);
    process.nextTick(function() { log(5); });
    process.nextTick(function() { log(6); });
  });
});

//1 4 2 3 5 6

Tại sao những kết quả này? Vui lòng giải thích bằng hình ảnh hoặc lời giải thích rất dễ làm theo. Ngay cả các nhà phát triển lõi của nút cũng không đồng ý với cách mọi người hiểu nextTick và setIm Instant.

Nguồn:


8
Câu hỏi tuyệt vời. Đây là một cách tuyệt vời để minh họa sự khác biệt.
— SystemParadox

8
Kết quả là như nhau ( 1 4 2 3 5 6) cho cả hành (Node v5.6.0)
— Deniz Ozger

Câu hỏi thông minh và hữu ích!
— Mehdi Raash

cả sản xuất cùng một sản lượng
— MechaCode

1
Hãy nhìn vào năm, 5 năm trước! Rất nhiều thứ có thể đã thay đổi.
— Gabriel Llamas

Câu trả lời:


105

Hãy xem xét hai ví dụ sau:

setIm Instant

setImmediate(function A() {
  setImmediate(function B() {
    log(1);
    setImmediate(function D() { log(2); });
    setImmediate(function E() { log(3); });
  });
  setImmediate(function C() {
    log(4);
    setImmediate(function F() { log(5); });
    setImmediate(function G() { log(6); });
  });
});

setTimeout(function timeout() {
  console.log('TIMEOUT FIRED');
}, 0)

// 'TIMEOUT FIRED' 1 4 2 3 5 6
// OR
// 1 'TIMEOUT FIRED' 4 2 3 5 6

nextTick

process.nextTick(function A() {
  process.nextTick(function B() {
    log(1);
    process.nextTick(function D() { log(2); });
    process.nextTick(function E() { log(3); });
  });
  process.nextTick(function C() {
    log(4);
    process.nextTick(function F() { log(5); });
    process.nextTick(function G() { log(6); });
  });
});

setTimeout(function timeout() {
  console.log('TIMEOUT FIRED');
}, 0)

// 1 4 2 3 5 6 'TIMEOUT FIRED'

Các lệnh gọi lại setIm ngay được kích hoạt khỏi vòng lặp sự kiện, một lần mỗi lần lặp theo thứ tự chúng đã được xếp hàng đợi. Vì vậy, trong lần lặp đầu tiên của vòng lặp sự kiện, lệnh gọi lại A được kích hoạt. Sau đó, vào lần lặp thứ hai của vòng lặp sự kiện, lệnh gọi lại B được kích hoạt, sau đó vào lần lặp thứ ba của vòng lặp sự kiện, lệnh gọi lại C được kích hoạt, v.v. Điều này ngăn vòng lặp sự kiện bị chặn và cho phép các lệnh gọi lại I / O hoặc bộ hẹn giờ khác được được gọi trong thời gian trung bình (như trường hợp thời gian chờ 0ms, được kích hoạt vào lần lặp vòng lặp thứ nhất hoặc thứ hai).

Tuy nhiên, lệnh gọi lại nextTick luôn được kích hoạt ngay sau khi mã hiện tại được thực thi xong và TRƯỚC KHI quay lại vòng lặp sự kiện. Trong ví dụ nextTick, chúng tôi kết thúc việc thực thi tất cả các lệnh gọi lại nextTick trước khi quay trở lại vòng lặp sự kiện. Vì lệnh gọi lại của setTimeout sẽ được gọi từ vòng lặp sự kiện, văn bản 'TIMEOUT FIRED' sẽ không được xuất cho đến khi chúng ta thực hiện xong mọi lệnh gọi lại nextTick.


8
Hoàn hảo, đoạn cuối là chìa khóa để hiểu sự khác biệt chính.
— Gabriel Llamas vào

ví dụ tiếp theo của bạnTick có đầu ra khác nhau:TIMEOUT FIRED 1 4 2 3 5 6
— Jürgen Paul

Bạn đang chạy phiên bản nút nào? Trên nút 0.10.x, bạn phải luôn chạy lệnh gọi lại nextTick trước khi thực hiện vòng lặp sự kiện.
— Dave Stibrany

23
Tôi ước họ sẽ chọn một cái tên khác. Ngay bây giờ "nextTick" tức thì hơn setIm Instant.
— xiên que

1
@StrugglingCoder Vì khi A thực thi, nó sẽ đặt B và C được lên lịch tiếp theo. Khi B thực thi, nó đặt D và E được lên lịch tiếp theo, nhưng C đã được lên lịch sau B. Hãy nhớ rằng có sự khác biệt giữa thời điểm cuộc gọi lại được lên lịch hoặc đăng ký và khi lệnh gọi lại thực sự kích hoạt.
— Dave Stibrany

25

Theo Node.js, tên tài liệu của hai hàm này được hoán đổi chính xác

setIm Instant () ( ĐƯỢC KHUYẾN NGHỊ TỐT NHẤT )

Nó cháy đầu tiên ở hàng đợi sự kiện


process.nextTick () ( SỬ DỤNG CHO CÁC TRƯỜNG HỢP ĐẶC BIỆT xem ví dụ sau )

Nó cháy ngay lập tức, nó giống như viết một tuyên bố nhiều hơn ở cuối tệp hiện tại


Nếu chúng tôi có mã này

setTimeout(function(){
  console.log('Hello world 5'); // It's waiting like a normal person at a queue
}, 0);

setImmediate(function(){
  console.log('Hello world 4'); 
  // It's like get to last and be take care of first 
  // but always after of .nextTick and before of setInterval(, 0)
});

process.nextTick(function(){
   console.log('Hello world 3'); // It's like be at the bottom at this file
});

console.log('Hello world 1');
console.log('Hello world 2');

Giải thích trực quan theo yêu cầu của bạn:

nhập mô tả hình ảnh ở đây

Các trường hợp sử dụng process.nextTick () khi bạn phải tạo và sự kiện trước để xử lý nó:

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(function () {
    this.emit('event');
  }.bind(this));
}
util.inherits(MyEmitter, EventEmitter);

const myEmitter = new MyEmitter();
myEmitter.on('event', function() {
  console.log('an event occurred!');
});

Hãy xem video này, nơi Philip Roberts cung cấp cho chúng ta lời giải thích tuyệt vời về vòng lặp sự kiện thời gian chạy và xem trình gỡ lỗi eventloop trực tuyến này Kiểm tra trực tiếp cách hoạt động của vòng lặp sự kiện

Nguồn: https://github.com/nodejs/node/blob/master/doc/topics/the-event-loop-timers-and-nexttick.md#processnexttick-vs-setim ngay


1
Câu trả lời này có ý nghĩa hơn! Cảm ơn @gsalgadotoledo: p bạn ơi, viết tên bạn còn khó hơn hiểu nextTick và setIm ngay.
— Aimal Khan

8

Tôi không thể tái tạo kết quả của bạn cho setImmediate. Nó phải giống như nextTick(và nó là trong các thử nghiệm của tôi) vì trong tình huống này, chúng làm khá nhiều điều tương tự. Lời giải thích hợp lý duy nhất cho điều đó là nó setImmediateđồng bộ bằng cách nào đó, nhưng nó không phải vậy.

Và theo tài liệu NodeJS , sự khác biệt thực sự duy nhất là nhiều nextTickcó thể kích hoạt trong một lần lặp vòng lặp (tùy thuộc vào maxTickDepth), trong khi setImmediatekích hoạt một lần mỗi lần lặp.


3

Dưới đây cung cấp cho bạn rõ ràng hơn.

setIm Instant

  1. Nó thực thi một tập lệnh sau khi giai đoạn thăm dò hiện tại hoàn tất.
  2. Đó là một chức năng mô-đun hẹn giờ và các chức năng hẹn giờ là toàn cầu, bạn có thể gọi chúng mà không cần require.
  3. Nó có thể được xóa bởi clearIm Instant ().
  4. Đặt thực thi lệnh gọi lại "ngay lập tức" sau lệnh gọi lại của sự kiện I / O trước setTimeout () và setInterval ().

nextTick

  1. Đó là một hàm đối tượng toàn cục của quá trình của NodeJS.
  2. Tất cả các lệnh gọi lại được chuyển đến process.nextTick () sẽ được giải quyết trước khi vòng lặp sự kiện tiếp tục.
  3. Cho phép người dùng xử lý lỗi.
  4. Giúp thử lại yêu cầu trước khi vòng lặp sự kiện tiếp tục.

Đoạn mã đơn giản.

console.log("I'm First");

setImmediate(function () {
  console.log('Im setImmediate');
});

console.log("I'm Second");

process.nextTick(function () {
  console.log('Im nextTick');
});

console.log("I'm Last");

/*
Output
$ node server.js
I'm First
I'm Second
I'm Last
Im nextTick
Im setImmediate
*/

2

Tôi nghĩ rằng tất cả các câu trả lời ở trên đã lỗi thời, bởi vì tôi liên tục nhận được các câu trả lời khác nhau với phiên bản nodejs hiện tại và rất dễ dàng để suy luận về

var log=console.log
log(process.version)

var makeAsyncCall
if(false)
    makeAsyncCall=setImmediate
else
    makeAsyncCall=process.nextTick;

makeAsyncCall(function A () {
    makeAsyncCall(function B() {
        log(1);
        makeAsyncCall(function C() { log(2); });
        makeAsyncCall(function D() { log(3); });
    });
    makeAsyncCall(function E() {
        log(4);
        makeAsyncCall(function F() { log(5); });
        makeAsyncCall(function G() { log(6); });
    });
});
//1
//4
//2
//3
//5
//6
//in both case

Sau khi đọc https://github.com/nodejs/node/blob/master/doc/topics/the-event-loop-timers-and-nexttick.md#processnexttick-vs-setim ngay lập tức hãy bắt đầu sử dụng setImmediatechúng tôi nên theo dõi các check queuevì nó là nơi setImmediatecư trú gọi lại.

Lần lặp đầu tiên

A được đẩy đến check queue

hàng đợi kiểm tra: [A]

Lần lặp thứ hai

Ađược rút ra từ queueđể thực hiện

Trong quá trình thực thi, nó đặt Bvà Eđến queuevà sau đó, Ahoàn thành và bắt đầu lần lặp tiếp theo

hàng đợi kiểm tra: [B, E]

Lần lặp thứ ba

kéo ra Bvà đẩyC D

hàng đợi kiểm tra: [E, C, D]

Lần lặp thứ 4

kéo ra Evà đẩyF G

hàng đợi kiểm tra: [C, D, F, G]

Cuối cùng

thực hiện các lệnh gọi lại trong hàng đợi một cách tuần tự

Đối với nextTicktrường hợp, hàng đợi hoạt động theo cùng một cách, đó là lý do tại sao nó tạo ra cùng một kết quả

Điều khác biệt là:

nextTickQueue sẽ được xử lý sau khi hoạt động hiện tại hoàn thành, bất kể giai đoạn hiện tại của vòng lặp sự kiện

Nói rõ hơn, vòng lặp sự kiện duy trì nhiều hàng đợi và check queuechỉ là một trong số đó, nút sẽ quyết định hàng đợi nào sẽ sử dụng dựa trên một số quy tắc

với process.nextTicktuy nhiên, nó là loại bỏ qua tất cả các quy tắc và thực hiện gọi lại ở nextTickngay

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.