if (mode == UV_RUN_ONCE) { /* UV_RUN_ONCE implies forward progress: at least one callback must have * been invoked when it returns. uv__io_poll() can return without doing * I/O (meaning: no callbacks) when its timeout expires - which means we * have pending timers that satisfy the forward progress constraint. * * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from * the check. */ // UV_RUN_ONCE 至少有一个回调执行,不然该循环就空转了,满足前进要求 // 这也是[文章](https://zehai.info/2020/04/10/2020-04-10-eventloop/)中写到: // poll为空,eventloop将检查timer是否有快到的,如果需要执行,eventloop将要进入timers阶段来顺序执行timer callback uv__update_time(loop); uv__run_timers(loop); }
r = uv__loop_alive(loop); if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT) break; }
/* The if statement lets gcc compile it to a conditional store. Avoids * dirtying a cache line. */ if (loop->stop_flag != 0) loop->stop_flag = 0;
// 不行了,看不懂了 voiduv__io_poll(uv_loop_t* loop, int timeout){ structpollfd events[1024]; structpollfd pqry; structpollfd* pe; structpoll_ctl pc; QUEUE* q; uv__io_t* w; uint64_t base; uint64_t diff; int have_signals; int nevents; int count; int nfds; int i; int rc; int add_failed;
if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); return; }
add_failed = 0; if (w->events == 0) { pc.cmd = PS_ADD; if (pollset_ctl(loop->backend_fd, &pc, 1)) { if (errno != EINVAL) { assert(0 && "Failed to add file descriptor (pc.fd) to pollset"); abort(); } /* Check if the fd is already in the pollset */ pqry.fd = pc.fd; rc = pollset_query(loop->backend_fd, &pqry); switch (rc) { case-1: assert(0 && "Failed to query pollset for file descriptor"); abort(); case0: assert(0 && "Pollset does not contain file descriptor"); abort(); } /* If we got here then the pollset already contained the file descriptor even though * we didn't think it should. This probably shouldn't happen, but we can continue. */ add_failed = 1; } } if (w->events != 0 || add_failed) { /* Modify, potentially removing events -- need to delete then add. * Could maybe mod if we knew for sure no events are removed, but * content of w->events is handled above as not reliable (falls back) * so may require a pollset_query() which would have to be pretty cheap * compared to a PS_DELETE to be worth optimizing. Alternatively, could * lazily remove events, squelching them in the mean time. */ pc.cmd = PS_DELETE; if (pollset_ctl(loop->backend_fd, &pc, 1)) { assert(0 && "Failed to delete file descriptor (pc.fd) from pollset"); abort(); } pc.cmd = PS_ADD; if (pollset_ctl(loop->backend_fd, &pc, 1)) { assert(0 && "Failed to add file descriptor (pc.fd) to pollset"); abort(); } }
w->events = w->pevents; }
assert(timeout >= -1); base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */
for (;;) { nfds = pollset_poll(loop->backend_fd, events, ARRAY_SIZE(events), timeout);
/* Update loop->time unconditionally. It's tempting to skip the update when * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the * operating system didn't reschedule our process while in the syscall. */ SAVE_ERRNO(uv__update_time(loop));
if (nfds == 0) { assert(timeout != -1); return; }
if (nfds == -1) { if (errno != EINTR) { abort(); }
if (timeout == -1) continue;
if (timeout == 0) return;
/* Interrupted by a signal. Update timeout and poll again. */ goto update_timeout; }
if (w == NULL) { /* File descriptor that we've stopped watching, disarm it. * * Ignore all errors because we may be racing with another thread * when the file descriptor is closed. */ pollset_ctl(loop->backend_fd, &pc, 1); continue; }
/* Run signal watchers last. This also affects child process watchers * because those are implemented in terms of signal watchers. */ if (w == &loop->signal_io_watcher) have_signals = 1; else w->cb(loop, w, pe->revents);
nevents++; }
if (have_signals != 0) loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
//lib/internal/process/task_queues.js // `nextTick()` will not enqueue any callback when the process is about to // exit since the callback would not have a chance to be executed. // 意思就是nextTick在进程快要结束时不会排队callback,因为没有机会执行 // 你们看引用的文档吧,我看不下去了😭 // 主要的思路是JS执行process.nexTick(),然后将callback交给c++执行 functionnextTick(callback) { if (typeof callback !== 'function') thrownewERR_INVALID_CALLBACK(callback);
This phase allows a person to execute callbacks immediately after the poll phase has completed. If the poll phase becomes idle and scripts have been queued with setImmediate(), the event loop may continue to the check phase rather than waiting.
setImmediate() is actually a special timer that runs in a separate phase of the event loop. It uses a libuv API that schedules callbacks to execute after the poll phase has completed.
Generally, as the code is executed, the event loop will eventually hit the poll phase where it will wait for an incoming connection, request, etc. However, if a callback has been scheduled with setImmediate() and the poll phase becomes idle, it will end and continue to the check phase rather than waiting for poll events.
fs.readFile 的回调函数执行完后:
注册 setTimeout 的回调函数到 timer 阶段
注册 setImmediate 的回调函数到 check 阶段
event loop 从 pool 阶段出来继续往下一个阶段执行,恰好是 check 阶段,所以 setImmediate 的回调函数先执行
TypeError: Chaining cycle detected for promise #<Promise> at <anonymous> at process._tickCallback (internal/process/next_tick.js:188:7) at Function.Module.runMain (module.js:667:11) at startup (bootstrap_node.js:187:16) at bootstrap_node.js:607:3