DEV Community

x1957
x1957

Posted on

1

nodejs event loop

最近遇到个问题,在for loop里面调用http.get请求,测试发现,会慢个几ms然后请求才会发出去,代码如下:


async function run() {
    for (let i = 0; i < 100; i++) {
        for (let j = 0; j < 40; j++) {
            let local_ts = Date.now();
            let cid = 'cid_' + i * 1000 + j;
            let url = `http://127.0.0.1:8890/ts?id=${cid}&ts=${local_ts}`;
            http.get(url).then(ret => {
                let server_ts = ret.split(' ')[1];
                let finish_ts = Date.now();
                console.log(cid, ret, 'elapsed', finish_ts - local_ts, 'server_diff', server_ts - local_ts, 'finish_diff', finish_ts - server_ts);
            });
            console.log(cid, 'fired', local_ts, Date.now());
        }
        await Sleep(3000);
    }
}

run();

调用的是个http服务,传本地的timestamp给他,他返回server的timestamp,方便我们计算网络的latency。

这时候就非常疑惑了,为什么server收到我们发送的请求都要6-10ms,这中间网络肯定没这么慢。

怀疑是event loop的锅,首先看了下http的实现,在最后会调用一个process.nextTick

libuv event loop

event loop里面分了很多个loop,代码可以参考

r = uv__loop_alive(loop);
  if (!r)
    uv__update_time(loop);

while (r != 0 && loop->stop_flag == 0) {
    uv__update_time(loop);
    uv__run_timers(loop);
    ran_pending = uv__run_pending(loop);
    uv__run_idle(loop);
    uv__run_prepare(loop);

    timeout = 0;
    if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
      timeout = uv_backend_timeout(loop);

    uv__io_poll(loop, timeout);
    uv__run_check(loop);
    uv__run_closing_handles(loop);

    if (mode == UV_RUN_ONCE) {
      uv__update_time(loop);
      uv__run_timers(loop);
    }

    r = uv__loop_alive(loop);
    if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
      break;
}

而process.nextTick是在这里面任意一个loop结束后就执行的,所以我们的请求其实都堆积到了sleep那儿,因为http get都在for loop里面,所以这里我们如果要立马执行http get,需要切出我们的loop回到event loop. 在循环立马加入一个await Sleep(0)


async function run() {
    for (let i = 0; i < 100; i++) {
        for (let j = 0; j < 40; j++) {
            let local_ts = Date.now();
            let cid = 'cid_' + i * 1000 + j;
            let url = `http://127.0.0.1:8890/ts?id=${cid}&ts=${local_ts}`;
            http.get(url).then(ret => {
                let server_ts = ret.split(' ')[1];
                let finish_ts = Date.now();
                console.log(cid, ret, 'elapsed', finish_ts - local_ts, 'server_diff', server_ts - local_ts, 'finish_diff', finish_ts - server_ts);
            });
            console.log(cid, 'fired', local_ts, Date.now());
            await Sleep(0);
        }
        await Sleep(3000);
    }
}

run();

这时候堆积之后再请求的问题就解决了,不过问题还是有,一毫秒左右才一个请求,感觉太慢。

PS. Sleep代码

const Promise = require('bluebird');
module.exports = function(duration) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(null);
        }, duration);
    });
}

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs