Подтвердить что ты не робот

Как работает setTimeout в node.js

Мне было интересно, знает ли кто-нибудь, как setTimeout реализован в node.js. Кажется, я где-то читал, что это не часть V8. Я быстро попытался найти реализацию, но не смог найти ее в источнике (BIG). Я нашел, например, файл timers.js, который затем, например, ссылается на timer_wrap.cc. Но этот файл не полностью отвечает на все мои вопросы.

  • Есть ли у V8 реализация setTimeout? Я думаю, также из источника ответа нет.
  • Как реализовано setTimeout? javascript или родной или сочетание обоих? Из timers.js я предполагаю что-то вдоль линии обоих:

    var Timer = process.binding('timer_wrap').Timer;`
    
  • При добавлении нескольких таймеров (setTimeout) как node.js знает, что выполнить в первую очередь? Добавляет ли он все таймеры в коллекцию (сортируется)? Если он отсортирован, то поиск тайм-аута, который должен быть выполнен, - это O (1) и O (log n) для вставки? Но опять же в timers.js я вижу, что они используют связанный список?

  • Но опять же добавление большого количества таймеров не является проблемой вообще?
  • При выполнении этого script:

    var x = new Array(1000),
        len = x.length;
    
    /**
     * Returns a random integer between min and max
     * Using Math.round() will give you a non-uniform distribution!
     */
    function getRandomInt (min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }
    
    var y = 0;
    
    for (var i = 0; i < len; i++) {
        var randomTimeout = getRandomInt(1000, 10000);
    
        console.log(i + ', ' + randomTimeout + ', ' + ++y);
        setTimeout(function () {
            console.log(arguments);
        }, randomTimeout, randomTimeout, y);
    }
    

    вы получаете немного загрузки процессора, но не так много?

  • Мне интересно, реализую ли я все эти обратные вызовы один за другим в отсортированном списке, если я получу лучшую производительность?
4b9b3361

Ответ 1

Вы уже проделали большую часть работы. V8 не обеспечивает реализацию для setTimeout, поскольку он не является частью ECMAScript. Функция, которую вы используете, реализована в timers.js, которая создает экземпляр объекта Timeout, который является оберткой вокруг класса C.

В источнике есть комментарий, описывающий, как они управляют таймерами.

// Because often many sockets will have the same idle timeout we will not
// use one timeout watcher per item. It is too much overhead.  Instead
// we'll use a single watcher for all sockets with the same timeout value
// and a linked list. This technique is described in the libev manual:
// http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Be_smart_about_timeouts

Это указывает на использование двойного связанного списка, который является # 4 в связанной статье.

Если нет ни одного запроса, но много тысяч (миллионов...), все используя какой-то тайм-аут с одинаковым значением тайм-аута, затем один может сделать еще лучше:

При запуске таймаута вычислите значение тайм-аута и установите тайм-аут в конце списка.

Затем используйте ev_timer для запуска, когда тайм-аут в начале (например, с использованием техники № 3).

Когда есть активность, удалите таймер из списка, пересчитать таймаут, снова добавить его в конец списка и обязательно обновите ev_timer, если он был взят с начала список.

Таким образом, можно управлять неограниченным количеством таймаутов в O (1) раз для запуска, остановки и обновления таймеров за счет серьезное осложнение и необходимость использования постоянного таймаута. Постоянная timeout гарантирует, что список будет отсортирован.

Node.js спроектирован вокруг асинхронных операций, а setTimeout является важной частью этого. Я бы не пытался запутаться, просто используйте то, что они предоставляют. Доверьтесь, что он достаточно быстро, пока вы не доказали, что в вашем конкретном случае это узкое место. Не зацикливайтесь на преждевременной оптимизации.

UPDATE

Что происходит, у вас есть по существу словарь тайм-аутов на верхнем уровне, поэтому все тайм-ауты в 100 мс группируются вместе. Когда добавляется новый тайм-аут или старшие триггеры таймаута, он добавляется к списку. Это означает, что самый старый тайм-аут, тот, который будет запускаться быстрее, находится в начале списка. Для этого списка есть один таймер, и он устанавливается на основе времени, пока первый элемент в списке не истечет.

Если вы вызываете setTimeout 1000 раз каждый с одинаковым значением таймаута, они будут добавлены в список в порядке, который вы назвали setTimeout, и сортировка не требуется. Это очень эффективная настройка.

Ответ 2

Нет проблем со многими таймерами! Когда uv loop call poll, он передает ему аргумент тайм-аута с ближайшим таймером всех таймеров.

[ближайший таймер всех таймеров]
https://github.com/joyent/node/blob/master/deps/uv/src/unix/timer.c # 120

RB_MIN(uv__timers, &loop->timer_handles)  

[передать аргумент таймаута для опроса api]
https://github.com/joyent/node/blob/master/deps/uv/src/unix/core.c # 276

timeout = 0;  
if ((mode & UV_RUN_NOWAIT) == 0)  
    timeout = uv_backend_timeout(loop);  

uv__io_poll(loop, timeout); 

Примечание: в ОС Windows это почти такая же логика