[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fbase\u002Fevent":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fbase\u002Fevent":1756,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fbase\u002Fevent":1779},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":14,"pinned":15,"draft":15,"body":16,"seo":1752,"stem":1753,"id":1754,"extension":1755},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fbase\u002Fevent","事件驱动与非阻塞 I\u002FO 模型","Node.js 的底层架构核心是事件驱动与非阻塞 I\u002FO：以 V8 引擎执行 JavaScript、libuv 库处理底层 I\u002FO 与事件循环。涵盖 libuv 线程池、事件循环各阶段、EventEmitter 实践、最佳实践与常见坑。","技能小册",[12,13],"nodejs","event","2020-06-20",false,{"type":17,"value":18,"toc":1740},"minimark",[19,23,44,48,66,70,82,121,315,319,345,435,613,634,742,746,761,772,968,972,989,1311,1360,1414,1418,1489,1492,1536,1539,1640,1643,1667,1670,1673,1736],[20,21,8],"h1",{"id":22},"事件驱动与非阻塞-io-模型",[24,25,26,27,31,32,35,36,39,40,43],"p",{},"Node.js 的底层架构就像一个「一人指挥，千人协作」的高效团队，其核心秘密就是",[28,29,30],"strong",{},"事件驱动","与",[28,33,34],{},"非阻塞 I\u002FO","。它让一个进程能够高效地处理海量并发任务。以 ",[28,37,38],{},"V8 引擎","（执行 JavaScript 代码）和 ",[28,41,42],{},"libuv 库","（处理底层 I\u002FO 与事件循环）为两大基石。",[45,46,47],"h2",{"id":47},"核心概念",[49,50,51,57],"ul",{},[52,53,54,56],"li",{},[28,55,30],{},"：这是一种程序设计范式，程序的执行流程由外部事件（如用户请求、文件读取完成）来决定。Node.js 启动后，会初始化一个事件循环，并注册各类事件的回调函数。",[52,58,59,61,62,65],{},[28,60,34],{},"：传统 I\u002FO 操作（如读取文件、查询数据库）会「阻塞」线程，直到操作完成才能执行后续代码。Node.js 中的所有 I\u002FO 操作（网络请求、文件读写等）",[28,63,64],{},"都是非阻塞的","，发起操作后立即返回，不会等待结果，避免主线程被「卡住」。",[45,67,69],{"id":68},"一libuvnodejs-的动力核心","一、libuv：Node.js 的「动力核心」",[24,71,72,73,76,77,81],{},"Node.js 看似单线程，但底层依赖 libuv 提供强大的多线程支持。libuv 使用",[28,74,75],{},"线程池","来处理无法异步执行的任务（如文件 I\u002FO、DNS 查找）。线程池默认大小为 4，可通过 ",[78,79,80],"code",{},"UV_THREADPOOL_SIZE"," 环境变量调整。除文件 I\u002FO 外，Node.js 将 CPU 密集型任务（加密、压缩、DNS 解析）也委托给 libuv 线程池，防止阻塞主线程。",[83,84,89],"pre",{"className":85,"code":86,"language":87,"meta":88,"style":88},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","# 调整 libuv 线程池大小（需在进程启动前设置）\nUV_THREADPOOL_SIZE=8 node server.js\n","bash","",[78,90,91,100],{"__ignoreMap":88},[92,93,96],"span",{"class":94,"line":95},"line",1,[92,97,99],{"class":98},"sutJx","# 调整 libuv 线程池大小（需在进程启动前设置）\n",[92,101,103,106,110,114,118],{"class":94,"line":102},2,[92,104,80],{"class":105},"su5hD",[92,107,109],{"class":108},"smGrS","=",[92,111,113],{"class":112},"s_sjI","8",[92,115,117],{"class":116},"sbgvK"," node",[92,119,120],{"class":112}," server.js\n",[83,122,126],{"className":123,"code":124,"language":125,"meta":88,"style":88},"language-js shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F 演示：文件 I\u002FO 走线程池，彼此并行，不阻塞主线程\nconst fs = require(\"fs\");\n\nconsole.log(\"开始读取文件\");\nfs.readFile(\".\u002Fbig-file-1.txt\", () => console.log(\"文件1读完\"));\nfs.readFile(\".\u002Fbig-file-2.txt\", () => console.log(\"文件2读完\"));\nconsole.log(\"已发起两个读取请求，主线程继续执行其他代码\");\n","js",[78,127,128,133,169,176,201,250,293],{"__ignoreMap":88},[92,129,130],{"class":94,"line":95},[92,131,132],{"class":98},"\u002F\u002F 演示：文件 I\u002FO 走线程池，彼此并行，不阻塞主线程\n",[92,134,135,139,143,146,150,153,157,160,162,165],{"class":94,"line":102},[92,136,138],{"class":137},"sbsja","const",[92,140,142],{"class":141},"s_hVV"," fs",[92,144,145],{"class":108}," =",[92,147,149],{"class":148},"sGLFI"," require",[92,151,152],{"class":105},"(",[92,154,156],{"class":155},"sjJ54","\"",[92,158,159],{"class":112},"fs",[92,161,156],{"class":155},[92,163,164],{"class":105},")",[92,166,168],{"class":167},"sP7_E",";\n",[92,170,172],{"class":94,"line":171},3,[92,173,175],{"emptyLinePlaceholder":174},true,"\n",[92,177,179,182,185,188,190,192,195,197,199],{"class":94,"line":178},4,[92,180,181],{"class":105},"console",[92,183,184],{"class":167},".",[92,186,187],{"class":148},"log",[92,189,152],{"class":105},[92,191,156],{"class":155},[92,193,194],{"class":112},"开始读取文件",[92,196,156],{"class":155},[92,198,164],{"class":105},[92,200,168],{"class":167},[92,202,204,206,208,211,213,215,218,220,223,226,229,232,234,236,238,240,243,245,248],{"class":94,"line":203},5,[92,205,159],{"class":105},[92,207,184],{"class":167},[92,209,210],{"class":148},"readFile",[92,212,152],{"class":105},[92,214,156],{"class":155},[92,216,217],{"class":112},".\u002Fbig-file-1.txt",[92,219,156],{"class":155},[92,221,222],{"class":167},",",[92,224,225],{"class":167}," ()",[92,227,228],{"class":137}," =>",[92,230,231],{"class":105}," console",[92,233,184],{"class":167},[92,235,187],{"class":148},[92,237,152],{"class":105},[92,239,156],{"class":155},[92,241,242],{"class":112},"文件1读完",[92,244,156],{"class":155},[92,246,247],{"class":105},"))",[92,249,168],{"class":167},[92,251,253,255,257,259,261,263,266,268,270,272,274,276,278,280,282,284,287,289,291],{"class":94,"line":252},6,[92,254,159],{"class":105},[92,256,184],{"class":167},[92,258,210],{"class":148},[92,260,152],{"class":105},[92,262,156],{"class":155},[92,264,265],{"class":112},".\u002Fbig-file-2.txt",[92,267,156],{"class":155},[92,269,222],{"class":167},[92,271,225],{"class":167},[92,273,228],{"class":137},[92,275,231],{"class":105},[92,277,184],{"class":167},[92,279,187],{"class":148},[92,281,152],{"class":105},[92,283,156],{"class":155},[92,285,286],{"class":112},"文件2读完",[92,288,156],{"class":155},[92,290,247],{"class":105},[92,292,168],{"class":167},[92,294,296,298,300,302,304,306,309,311,313],{"class":94,"line":295},7,[92,297,181],{"class":105},[92,299,184],{"class":167},[92,301,187],{"class":148},[92,303,152],{"class":105},[92,305,156],{"class":155},[92,307,308],{"class":112},"已发起两个读取请求，主线程继续执行其他代码",[92,310,156],{"class":155},[92,312,164],{"class":105},[92,314,168],{"class":167},[45,316,318],{"id":317},"二事件循环非阻塞-io-的调度中心","二、事件循环：非阻塞 I\u002FO 的「调度中心」",[24,320,321,322,325,326,325,329,325,332,325,335,325,338,341,342,344],{},"Node.js 中，用户编写的所有 JavaScript 回调函数都运行在单一线程上，这个线程就是事件循环自身。事件循环使其执行完一个简单任务后不用干等耗时 I\u002FO，而是通过多个阶段来调度任务。它按顺序执行 ",[78,323,324],{},"timers","、",[78,327,328],{},"pending callbacks",[78,330,331],{},"idle\u002Fprepare",[78,333,334],{},"poll",[78,336,337],{},"check",[78,339,340],{},"close callbacks","。特别关键的 ",[78,343,334],{}," 阶段会检索新的 I\u002FO 事件，并执行 I\u002FO 相关回调。",[346,347,348,361],"table",{},[349,350,351],"thead",{},[352,353,354,358],"tr",{},[355,356,357],"th",{},"阶段",[355,359,360],{},"作用",[362,363,364,382,391,401,410,422],"tbody",{},[352,365,366,371],{},[367,368,369],"td",{},[78,370,324],{},[367,372,373,374,377,378,381],{},"执行到期的 ",[78,375,376],{},"setTimeout","\u002F",[78,379,380],{},"setInterval"," 回调",[352,383,384,388],{},[367,385,386],{},[78,387,328],{},[367,389,390],{},"执行上一轮延迟到本轮的 I\u002FO 回调",[352,392,393,398],{},[367,394,395],{},[78,396,397],{},"idle, prepare",[367,399,400],{},"内部使用，一般无需关注",[352,402,403,407],{},[367,404,405],{},[78,406,334],{},[367,408,409],{},"检索新的 I\u002FO 事件；有回调时执行，若为空且无 timer 会在此阻塞等待",[352,411,412,416],{},[367,413,414],{},[78,415,337],{},[367,417,418,419,381],{},"执行 ",[78,420,421],{},"setImmediate",[352,423,424,428],{},[367,425,426],{},[78,427,340],{},[367,429,430,431,434],{},"执行如 ",[78,432,433],{},"socket.on('close', ...)"," 一类的关闭回调",[83,436,438],{"className":123,"code":437,"language":125,"meta":88,"style":88},"setTimeout(() => console.log(\"timer\"), 0);\nsetImmediate(() => console.log(\"immediate\"));\n\nfs.readFile(__filename, () => {\n  \u002F\u002F 在 I\u002FO 回调内部，setImmediate 一定先于 setTimeout 执行（顺序更确定）\n  setTimeout(() => console.log(\"inner timer\"), 0);\n  setImmediate(() => console.log(\"inner immediate\"));\n});\n",[78,439,440,478,507,511,531,536,573,603],{"__ignoreMap":88},[92,441,442,444,446,449,451,453,455,457,459,461,464,466,468,470,474,476],{"class":94,"line":95},[92,443,376],{"class":148},[92,445,152],{"class":105},[92,447,448],{"class":167},"()",[92,450,228],{"class":137},[92,452,231],{"class":105},[92,454,184],{"class":167},[92,456,187],{"class":148},[92,458,152],{"class":105},[92,460,156],{"class":155},[92,462,463],{"class":112},"timer",[92,465,156],{"class":155},[92,467,164],{"class":105},[92,469,222],{"class":167},[92,471,473],{"class":472},"srdBf"," 0",[92,475,164],{"class":105},[92,477,168],{"class":167},[92,479,480,482,484,486,488,490,492,494,496,498,501,503,505],{"class":94,"line":102},[92,481,421],{"class":148},[92,483,152],{"class":105},[92,485,448],{"class":167},[92,487,228],{"class":137},[92,489,231],{"class":105},[92,491,184],{"class":167},[92,493,187],{"class":148},[92,495,152],{"class":105},[92,497,156],{"class":155},[92,499,500],{"class":112},"immediate",[92,502,156],{"class":155},[92,504,247],{"class":105},[92,506,168],{"class":167},[92,508,509],{"class":94,"line":171},[92,510,175],{"emptyLinePlaceholder":174},[92,512,513,515,517,519,522,524,526,528],{"class":94,"line":178},[92,514,159],{"class":105},[92,516,184],{"class":167},[92,518,210],{"class":148},[92,520,521],{"class":105},"(__filename",[92,523,222],{"class":167},[92,525,225],{"class":167},[92,527,228],{"class":137},[92,529,530],{"class":167}," {\n",[92,532,533],{"class":94,"line":203},[92,534,535],{"class":98},"  \u002F\u002F 在 I\u002FO 回调内部，setImmediate 一定先于 setTimeout 执行（顺序更确定）\n",[92,537,538,541,544,546,548,550,552,554,556,558,561,563,565,567,569,571],{"class":94,"line":252},[92,539,540],{"class":148},"  setTimeout",[92,542,152],{"class":543},"skxfh",[92,545,448],{"class":167},[92,547,228],{"class":137},[92,549,231],{"class":105},[92,551,184],{"class":167},[92,553,187],{"class":148},[92,555,152],{"class":543},[92,557,156],{"class":155},[92,559,560],{"class":112},"inner timer",[92,562,156],{"class":155},[92,564,164],{"class":543},[92,566,222],{"class":167},[92,568,473],{"class":472},[92,570,164],{"class":543},[92,572,168],{"class":167},[92,574,575,578,580,582,584,586,588,590,592,594,597,599,601],{"class":94,"line":295},[92,576,577],{"class":148},"  setImmediate",[92,579,152],{"class":543},[92,581,448],{"class":167},[92,583,228],{"class":137},[92,585,231],{"class":105},[92,587,184],{"class":167},[92,589,187],{"class":148},[92,591,152],{"class":543},[92,593,156],{"class":155},[92,595,596],{"class":112},"inner immediate",[92,598,156],{"class":155},[92,600,247],{"class":543},[92,602,168],{"class":167},[92,604,606,609,611],{"class":94,"line":605},8,[92,607,608],{"class":167},"}",[92,610,164],{"class":105},[92,612,168],{"class":167},[24,614,615,622,623,626,627,629,630,633],{},[28,616,617,618,621],{},"微任务（",[78,619,620],{},"process.nextTick"," \u002F Promise）"," 会在",[28,624,625],{},"每个阶段之间","被清空执行，",[78,628,620],{}," 的优先级高于 Promise 的 ",[78,631,632],{},".then"," 回调：",[83,635,637],{"className":123,"code":636,"language":125,"meta":88,"style":88},"process.nextTick(() => console.log(\"nextTick\"));\nPromise.resolve().then(() => console.log(\"promise\"));\nconsole.log(\"同步代码\");\n\u002F\u002F 输出顺序：同步代码 → nextTick → promise\n",[78,638,639,673,716,737],{"__ignoreMap":88},[92,640,641,644,646,649,651,653,655,657,659,661,663,665,667,669,671],{"class":94,"line":95},[92,642,643],{"class":105},"process",[92,645,184],{"class":167},[92,647,648],{"class":148},"nextTick",[92,650,152],{"class":105},[92,652,448],{"class":167},[92,654,228],{"class":137},[92,656,231],{"class":105},[92,658,184],{"class":167},[92,660,187],{"class":148},[92,662,152],{"class":105},[92,664,156],{"class":155},[92,666,648],{"class":112},[92,668,156],{"class":155},[92,670,247],{"class":105},[92,672,168],{"class":167},[92,674,675,679,681,684,686,688,691,693,695,697,699,701,703,705,707,710,712,714],{"class":94,"line":102},[92,676,678],{"class":677},"sZMiF","Promise",[92,680,184],{"class":167},[92,682,683],{"class":148},"resolve",[92,685,448],{"class":105},[92,687,184],{"class":167},[92,689,690],{"class":148},"then",[92,692,152],{"class":105},[92,694,448],{"class":167},[92,696,228],{"class":137},[92,698,231],{"class":105},[92,700,184],{"class":167},[92,702,187],{"class":148},[92,704,152],{"class":105},[92,706,156],{"class":155},[92,708,709],{"class":112},"promise",[92,711,156],{"class":155},[92,713,247],{"class":105},[92,715,168],{"class":167},[92,717,718,720,722,724,726,728,731,733,735],{"class":94,"line":171},[92,719,181],{"class":105},[92,721,184],{"class":167},[92,723,187],{"class":148},[92,725,152],{"class":105},[92,727,156],{"class":155},[92,729,730],{"class":112},"同步代码",[92,732,156],{"class":155},[92,734,164],{"class":105},[92,736,168],{"class":167},[92,738,739],{"class":94,"line":178},[92,740,741],{"class":98},"\u002F\u002F 输出顺序：同步代码 → nextTick → promise\n",[45,743,745],{"id":744},"三单线程与多线程的真相并发-vs-并行","三、「单线程」与「多线程」的真相：并发 vs 并行",[24,747,748,749,752,753,756,757,760],{},"理解 Node.js 如何处理并发，关键要分清",[28,750,751],{},"并发","（同时处理多件事）和",[28,754,755],{},"并行","（同时做多件事）。Node.js 利用其单线程事件循环模型处理请求，实现",[28,758,759],{},"高并发","。这种设计避免了多线程编程中的死锁、资源竞争等问题，极大简化了并发编程的复杂性。",[24,762,763,764,767,768,771],{},"CPU 密集型任务（如同步的大循环计算）",[28,765,766],{},"不会","被自动分流到线程池，会直接阻塞事件循环，此时应显式使用 ",[28,769,770],{},"Worker Threads","：",[83,773,775],{"className":123,"code":774,"language":125,"meta":88,"style":88},"\u002F\u002F worker.js\nconst { parentPort, workerData } = require(\"worker_threads\");\nparentPort.postMessage(heavyCompute(workerData));\n\n\u002F\u002F main.js\nconst { Worker } = require(\"worker_threads\");\nconst worker = new Worker(\".\u002Fworker.js\", { workerData: input });\nworker.on(\"message\", (result) => console.log(\"计算结果\", result));\n",[78,776,777,782,817,837,841,846,873,914],{"__ignoreMap":88},[92,778,779],{"class":94,"line":95},[92,780,781],{"class":98},"\u002F\u002F worker.js\n",[92,783,784,786,789,792,794,797,800,802,804,806,808,811,813,815],{"class":94,"line":102},[92,785,138],{"class":137},[92,787,788],{"class":167}," {",[92,790,791],{"class":141}," parentPort",[92,793,222],{"class":167},[92,795,796],{"class":141}," workerData",[92,798,799],{"class":167}," }",[92,801,145],{"class":108},[92,803,149],{"class":148},[92,805,152],{"class":105},[92,807,156],{"class":155},[92,809,810],{"class":112},"worker_threads",[92,812,156],{"class":155},[92,814,164],{"class":105},[92,816,168],{"class":167},[92,818,819,822,824,827,829,832,835],{"class":94,"line":171},[92,820,821],{"class":105},"parentPort",[92,823,184],{"class":167},[92,825,826],{"class":148},"postMessage",[92,828,152],{"class":105},[92,830,831],{"class":148},"heavyCompute",[92,833,834],{"class":105},"(workerData))",[92,836,168],{"class":167},[92,838,839],{"class":94,"line":178},[92,840,175],{"emptyLinePlaceholder":174},[92,842,843],{"class":94,"line":203},[92,844,845],{"class":98},"\u002F\u002F main.js\n",[92,847,848,850,852,855,857,859,861,863,865,867,869,871],{"class":94,"line":252},[92,849,138],{"class":137},[92,851,788],{"class":167},[92,853,854],{"class":141}," Worker",[92,856,799],{"class":167},[92,858,145],{"class":108},[92,860,149],{"class":148},[92,862,152],{"class":105},[92,864,156],{"class":155},[92,866,810],{"class":112},[92,868,156],{"class":155},[92,870,164],{"class":105},[92,872,168],{"class":167},[92,874,875,877,880,882,885,887,889,891,894,896,898,900,902,905,908,910,912],{"class":94,"line":295},[92,876,138],{"class":137},[92,878,879],{"class":141}," worker",[92,881,145],{"class":108},[92,883,884],{"class":108}," new",[92,886,854],{"class":148},[92,888,152],{"class":105},[92,890,156],{"class":155},[92,892,893],{"class":112},".\u002Fworker.js",[92,895,156],{"class":155},[92,897,222],{"class":167},[92,899,788],{"class":167},[92,901,796],{"class":543},[92,903,904],{"class":167},":",[92,906,907],{"class":105}," input ",[92,909,608],{"class":167},[92,911,164],{"class":105},[92,913,168],{"class":167},[92,915,916,919,921,924,926,928,931,933,935,938,942,944,946,948,950,952,954,956,959,961,963,966],{"class":94,"line":605},[92,917,918],{"class":105},"worker",[92,920,184],{"class":167},[92,922,923],{"class":148},"on",[92,925,152],{"class":105},[92,927,156],{"class":155},[92,929,930],{"class":112},"message",[92,932,156],{"class":155},[92,934,222],{"class":167},[92,936,937],{"class":167}," (",[92,939,941],{"class":940},"s99_P","result",[92,943,164],{"class":167},[92,945,228],{"class":137},[92,947,231],{"class":105},[92,949,184],{"class":167},[92,951,187],{"class":148},[92,953,152],{"class":105},[92,955,156],{"class":155},[92,957,958],{"class":112},"计算结果",[92,960,156],{"class":155},[92,962,222],{"class":167},[92,964,965],{"class":105}," result))",[92,967,168],{"class":167},[45,969,971],{"id":970},"四eventemitter事件驱动编程的基础-api","四、EventEmitter：事件驱动编程的基础 API",[24,973,974,975,325,978,325,981,984,985,988],{},"Node.js 大量内置模块（",[78,976,977],{},"http.Server",[78,979,980],{},"fs.ReadStream",[78,982,983],{},"net.Socket","）都继承自 ",[78,986,987],{},"EventEmitter","，也可以自己实现基于事件的模块：",[83,990,992],{"className":123,"code":991,"language":125,"meta":88,"style":88},"const { EventEmitter } = require(\"events\");\n\nclass OrderService extends EventEmitter {\n  createOrder(order) {\n    \u002F\u002F 业务逻辑完成后发出事件，通知所有关心「订单创建」的模块\n    this.emit(\"order:created\", order);\n  }\n}\n\nconst orders = new OrderService();\n\norders.on(\"order:created\", (order) => {\n  console.log(\"发送下单通知\", order.id);\n});\norders.once(\"order:created\", (order) => {\n  console.log(\"只统计一次首单事件\", order.id); \u002F\u002F once：只触发一次后自动移除监听\n});\n\norders.createOrder({ id: \"1001\" });\n",[78,993,994,1022,1026,1041,1056,1061,1090,1095,1100,1105,1123,1128,1158,1190,1199,1229,1263,1272,1277],{"__ignoreMap":88},[92,995,996,998,1000,1003,1005,1007,1009,1011,1013,1016,1018,1020],{"class":94,"line":95},[92,997,138],{"class":137},[92,999,788],{"class":167},[92,1001,1002],{"class":141}," EventEmitter",[92,1004,799],{"class":167},[92,1006,145],{"class":108},[92,1008,149],{"class":148},[92,1010,152],{"class":105},[92,1012,156],{"class":155},[92,1014,1015],{"class":112},"events",[92,1017,156],{"class":155},[92,1019,164],{"class":105},[92,1021,168],{"class":167},[92,1023,1024],{"class":94,"line":102},[92,1025,175],{"emptyLinePlaceholder":174},[92,1027,1028,1031,1034,1037,1039],{"class":94,"line":171},[92,1029,1030],{"class":137},"class",[92,1032,1033],{"class":116}," OrderService",[92,1035,1036],{"class":137}," extends",[92,1038,1002],{"class":116},[92,1040,530],{"class":167},[92,1042,1043,1047,1049,1052,1054],{"class":94,"line":178},[92,1044,1046],{"class":1045},"sVXei","  createOrder",[92,1048,152],{"class":167},[92,1050,1051],{"class":940},"order",[92,1053,164],{"class":167},[92,1055,530],{"class":167},[92,1057,1058],{"class":94,"line":203},[92,1059,1060],{"class":98},"    \u002F\u002F 业务逻辑完成后发出事件，通知所有关心「订单创建」的模块\n",[92,1062,1063,1067,1069,1072,1074,1076,1079,1081,1083,1086,1088],{"class":94,"line":252},[92,1064,1066],{"class":1065},"s39Yj","    this",[92,1068,184],{"class":167},[92,1070,1071],{"class":148},"emit",[92,1073,152],{"class":543},[92,1075,156],{"class":155},[92,1077,1078],{"class":112},"order:created",[92,1080,156],{"class":155},[92,1082,222],{"class":167},[92,1084,1085],{"class":105}," order",[92,1087,164],{"class":543},[92,1089,168],{"class":167},[92,1091,1092],{"class":94,"line":295},[92,1093,1094],{"class":167},"  }\n",[92,1096,1097],{"class":94,"line":605},[92,1098,1099],{"class":167},"}\n",[92,1101,1103],{"class":94,"line":1102},9,[92,1104,175],{"emptyLinePlaceholder":174},[92,1106,1108,1110,1113,1115,1117,1119,1121],{"class":94,"line":1107},10,[92,1109,138],{"class":137},[92,1111,1112],{"class":141}," orders",[92,1114,145],{"class":108},[92,1116,884],{"class":108},[92,1118,1033],{"class":148},[92,1120,448],{"class":105},[92,1122,168],{"class":167},[92,1124,1126],{"class":94,"line":1125},11,[92,1127,175],{"emptyLinePlaceholder":174},[92,1129,1131,1134,1136,1138,1140,1142,1144,1146,1148,1150,1152,1154,1156],{"class":94,"line":1130},12,[92,1132,1133],{"class":105},"orders",[92,1135,184],{"class":167},[92,1137,923],{"class":148},[92,1139,152],{"class":105},[92,1141,156],{"class":155},[92,1143,1078],{"class":112},[92,1145,156],{"class":155},[92,1147,222],{"class":167},[92,1149,937],{"class":167},[92,1151,1051],{"class":940},[92,1153,164],{"class":167},[92,1155,228],{"class":137},[92,1157,530],{"class":167},[92,1159,1161,1164,1166,1168,1170,1172,1175,1177,1179,1181,1183,1186,1188],{"class":94,"line":1160},13,[92,1162,1163],{"class":105},"  console",[92,1165,184],{"class":167},[92,1167,187],{"class":148},[92,1169,152],{"class":543},[92,1171,156],{"class":155},[92,1173,1174],{"class":112},"发送下单通知",[92,1176,156],{"class":155},[92,1178,222],{"class":167},[92,1180,1085],{"class":105},[92,1182,184],{"class":167},[92,1184,1185],{"class":105},"id",[92,1187,164],{"class":543},[92,1189,168],{"class":167},[92,1191,1193,1195,1197],{"class":94,"line":1192},14,[92,1194,608],{"class":167},[92,1196,164],{"class":105},[92,1198,168],{"class":167},[92,1200,1202,1204,1206,1209,1211,1213,1215,1217,1219,1221,1223,1225,1227],{"class":94,"line":1201},15,[92,1203,1133],{"class":105},[92,1205,184],{"class":167},[92,1207,1208],{"class":148},"once",[92,1210,152],{"class":105},[92,1212,156],{"class":155},[92,1214,1078],{"class":112},[92,1216,156],{"class":155},[92,1218,222],{"class":167},[92,1220,937],{"class":167},[92,1222,1051],{"class":940},[92,1224,164],{"class":167},[92,1226,228],{"class":137},[92,1228,530],{"class":167},[92,1230,1232,1234,1236,1238,1240,1242,1245,1247,1249,1251,1253,1255,1257,1260],{"class":94,"line":1231},16,[92,1233,1163],{"class":105},[92,1235,184],{"class":167},[92,1237,187],{"class":148},[92,1239,152],{"class":543},[92,1241,156],{"class":155},[92,1243,1244],{"class":112},"只统计一次首单事件",[92,1246,156],{"class":155},[92,1248,222],{"class":167},[92,1250,1085],{"class":105},[92,1252,184],{"class":167},[92,1254,1185],{"class":105},[92,1256,164],{"class":543},[92,1258,1259],{"class":167},";",[92,1261,1262],{"class":98}," \u002F\u002F once：只触发一次后自动移除监听\n",[92,1264,1266,1268,1270],{"class":94,"line":1265},17,[92,1267,608],{"class":167},[92,1269,164],{"class":105},[92,1271,168],{"class":167},[92,1273,1275],{"class":94,"line":1274},18,[92,1276,175],{"emptyLinePlaceholder":174},[92,1278,1280,1282,1284,1287,1289,1292,1295,1297,1300,1303,1305,1307,1309],{"class":94,"line":1279},19,[92,1281,1133],{"class":105},[92,1283,184],{"class":167},[92,1285,1286],{"class":148},"createOrder",[92,1288,152],{"class":105},[92,1290,1291],{"class":167},"{",[92,1293,1294],{"class":543}," id",[92,1296,904],{"class":167},[92,1298,1299],{"class":155}," \"",[92,1301,1302],{"class":112},"1001",[92,1304,156],{"class":155},[92,1306,799],{"class":167},[92,1308,164],{"class":105},[92,1310,168],{"class":167},[49,1312,1313,1334,1345],{},[52,1314,1315,1319,1320,1324,1325,1333],{},[28,1316,1317],{},[78,1318,923],{},"：注册持续监听；",[28,1321,1322],{},[78,1323,1208],{},"：只监听一次；",[28,1326,1327,377,1330],{},[78,1328,1329],{},"off",[78,1331,1332],{},"removeListener","：移除监听，避免内存泄漏。",[52,1335,1336,1337,1340,1341,1344],{},"默认单个事件最多 ",[28,1338,1339],{},"10 个监听器","，超过会打印警告，可通过 ",[78,1342,1343],{},"emitter.setMaxListeners(n)"," 调整（提示可能存在监听器泄漏）。",[52,1346,1347,1348,1351,1352,1355,1356,1359],{},"建议为",[28,1349,1350],{},"错误事件","统一监听 ",[78,1353,1354],{},"'error'","，否则未处理的 ",[78,1357,1358],{},"error"," 事件会直接抛出并可能导致进程崩溃。",[83,1361,1363],{"className":123,"code":1362,"language":125,"meta":88,"style":88},"orders.on(\"error\", (err) => console.error(\"订单服务出错：\", err));\n",[78,1364,1365],{"__ignoreMap":88},[92,1366,1367,1369,1371,1373,1375,1377,1379,1381,1383,1385,1388,1390,1392,1394,1396,1398,1400,1402,1405,1407,1409,1412],{"class":94,"line":95},[92,1368,1133],{"class":105},[92,1370,184],{"class":167},[92,1372,923],{"class":148},[92,1374,152],{"class":105},[92,1376,156],{"class":155},[92,1378,1358],{"class":112},[92,1380,156],{"class":155},[92,1382,222],{"class":167},[92,1384,937],{"class":167},[92,1386,1387],{"class":940},"err",[92,1389,164],{"class":167},[92,1391,228],{"class":137},[92,1393,231],{"class":105},[92,1395,184],{"class":167},[92,1397,1358],{"class":148},[92,1399,152],{"class":105},[92,1401,156],{"class":155},[92,1403,1404],{"class":112},"订单服务出错：",[92,1406,156],{"class":155},[92,1408,222],{"class":167},[92,1410,1411],{"class":105}," err))",[92,1413,168],{"class":167},[45,1415,1417],{"id":1416},"五总结nodejs-架构的多线程协作模型","五、总结：Node.js 架构的多线程协作模型",[346,1419,1420,1434],{},[349,1421,1422],{},[352,1423,1424,1428,1431],{},[355,1425,1427],{"align":1426},"left","组件",[355,1429,1430],{"align":1426},"类型",[355,1432,1433],{"align":1426},"主要任务",[362,1435,1436,1448,1464,1477],{},[352,1437,1438,1442,1445],{},[367,1439,1440],{"align":1426},[28,1441,38],{},[367,1443,1444],{"align":1426},"单线程（主线程）",[367,1446,1447],{"align":1426},"执行 JavaScript 代码（初始化、回调），是整个应用的控制中心。",[352,1449,1450,1455,1457],{},[367,1451,1452],{"align":1426},[28,1453,1454],{},"事件循环",[367,1456,1444],{"align":1426},[367,1458,1459,1460,1463],{"align":1426},"与 V8 共用线程，负责持续检查并分发任务，是 ",[78,1461,1462],{},"libuv"," 的核心。",[352,1465,1466,1471,1474],{},[367,1467,1468],{"align":1426},[28,1469,1470],{},"libuv 线程池",[367,1472,1473],{"align":1426},"多线程",[367,1475,1476],{"align":1426},"执行 Node.js 委托的异步阻塞操作，如文件 I\u002FO、DNS 查询等。",[352,1478,1479,1483,1486],{},[367,1480,1481],{"align":1426},[28,1482,770],{},[367,1484,1485],{"align":1426},"可选的多线程",[367,1487,1488],{"align":1426},"专门用于执行 CPU 密集型任务，提供真正的并行 JavaScript 执行能力。",[45,1490,1491],{"id":1491},"最佳实践",[49,1493,1494,1500,1509,1515,1526],{},[52,1495,1496,1497,1499],{},"CPU 密集型逻辑（图片处理、加密、复杂计算）迁移到 ",[28,1498,770],{}," 或独立进程，避免阻塞事件循环导致所有请求延迟。",[52,1501,1502,1503,1505,1506,1508],{},"自定义 ",[78,1504,987],{}," 子类务必监听 ",[78,1507,1354],{}," 事件，防止未捕获错误使进程崩溃。",[52,1510,1511,1512,1514],{},"大量文件\u002F网络 I\u002FO 场景下，根据实测适当调整 ",[78,1513,80],{},"（默认 4 常成为高并发文件读写的瓶颈）。",[52,1516,1517,1518,1521,1522,1525],{},"用 ",[78,1519,1520],{},"async\u002Fawait"," + Promise 包装回调式 API（",[78,1523,1524],{},"util.promisify","），减少深层回调嵌套，同时不改变其非阻塞本质。",[52,1527,1528,1529,325,1532,1535],{},"排查性能问题时用 ",[78,1530,1531],{},"--prof",[78,1533,1534],{},"clinic.js"," 等工具分析事件循环阻塞点，而非凭感觉猜测。",[45,1537,1538],{"id":1538},"常见坑",[346,1540,1541,1554],{},[349,1542,1543],{},[352,1544,1545,1548,1551],{},[355,1546,1547],{},"现象",[355,1549,1550],{},"常见原因",[355,1552,1553],{},"处理",[362,1555,1556,1567,1587,1604,1621],{},[352,1557,1558,1561,1564],{},[367,1559,1560],{},"所有请求突然变慢",[367,1562,1563],{},"同步代码里有耗时循环\u002FJSON.parse 大文件，阻塞事件循环",[367,1565,1566],{},"拆分为 Worker Threads 或分批异步处理",[352,1568,1569,1572,1577],{},[367,1570,1571],{},"内存持续增长，最终 OOM",[367,1573,1574,1576],{},[78,1575,987],{}," 监听器未清理，闭包持有大对象",[367,1578,1579,1580,377,1582,1584,1585],{},"及时 ",[78,1581,1329],{},[78,1583,1332],{},"，或用 ",[78,1586,1208],{},[352,1588,1589,1595,1601],{},[367,1590,1591,1594],{},[78,1592,1593],{},"MaxListenersExceededWarning"," 警告",[367,1596,1597,1598,1600],{},"同一事件被反复 ",[78,1599,923],{},"（如循环内注册）",[367,1602,1603],{},"检查监听器注册位置，避免重复注册",[352,1605,1606,1609,1615],{},[367,1607,1608],{},"自定义 EventEmitter 抛出未捕获异常导致进程退出",[367,1610,1611,1612,1614],{},"未监听 ",[78,1613,1354],{}," 事件",[367,1616,1617,1618,1620],{},"显式监听 ",[78,1619,1354],{}," 并处理",[352,1622,1623,1632,1635],{},[367,1624,1625,1628,1629,1631],{},[78,1626,1627],{},"setTimeout(fn, 0)"," 与 ",[78,1630,421],{}," 顺序不确定",[367,1633,1634],{},"在主模块顶层执行时二者顺序依赖具体运行环境",[367,1636,1637,1638],{},"若顺序重要，放入 I\u002FO 回调内部（此时顺序确定）或用 ",[78,1639,620],{},[45,1641,1642],{"id":1642},"延伸阅读",[49,1644,1645,1653,1660],{},[52,1646,1647,1652],{},[1648,1649,1651],"a",{"href":1650},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fbase\u002Fevent-loop","事件循环 event-loop","：更细粒度的阶段与微任务时序",[52,1654,1655,1659],{},[1648,1656,1658],{"href":1657},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fbun-deno-node","Bun \u002F Deno \u002F Node 对比","：不同运行时的事件模型差异",[52,1661,1662,1666],{},[1648,1663,1665],{"href":1664},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fcore\u002Fstream","核心模块 · stream","：基于事件的流式处理",[45,1668,1669],{"id":1669},"参考文献",[24,1671,1672],{},"以下链接在编写时均可正常访问：",[346,1674,1675,1685],{},[349,1676,1677],{},[352,1678,1679,1682],{},[355,1680,1681],{},"资料",[355,1683,1684],{},"说明",[362,1686,1687,1700,1712,1724],{},[352,1688,1689,1697],{},[367,1690,1691],{},[1648,1692,1696],{"href":1693,"rel":1694},"https:\u002F\u002Fnodejs.org\u002Fen\u002Fdocs\u002Fguides\u002Fevent-loop-timers-and-nexttick\u002F",[1695],"nofollow","Node.js：事件循环",[367,1698,1699],{},"官方指南",[352,1701,1702,1709],{},[367,1703,1704],{},[1648,1705,1708],{"href":1706,"rel":1707},"https:\u002F\u002Fnodejs.org\u002Fapi\u002Fevents.html",[1695],"Node.js：events",[367,1710,1711],{},"EventEmitter API",[352,1713,1714,1721],{},[367,1715,1716],{},[1648,1717,1720],{"href":1718,"rel":1719},"https:\u002F\u002Fnodejs.org\u002Fapi\u002Fworker_threads.html",[1695],"Node.js：worker_threads",[367,1722,1723],{},"Worker Threads API",[352,1725,1726,1733],{},[367,1727,1728],{},[1648,1729,1732],{"href":1730,"rel":1731},"https:\u002F\u002Fdocs.libuv.org\u002F",[1695],"libuv 文档",[367,1734,1735],{},"底层库",[1737,1738,1739],"style",{},"html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":88,"searchDepth":102,"depth":102,"links":1741},[1742,1743,1744,1745,1746,1747,1748,1749,1750,1751],{"id":47,"depth":102,"text":47},{"id":68,"depth":102,"text":69},{"id":317,"depth":102,"text":318},{"id":744,"depth":102,"text":745},{"id":970,"depth":102,"text":971},{"id":1416,"depth":102,"text":1417},{"id":1491,"depth":102,"text":1491},{"id":1538,"depth":102,"text":1538},{"id":1642,"depth":102,"text":1642},{"id":1669,"depth":102,"text":1669},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fend\u002FNodeJs\u002Fbase\u002Fevent","knowledge\u002Fknowledge\u002Fbooklet\u002Fend\u002FNodeJs\u002Fbase\u002Fevent.md","md",{"items":1757},[1758,1762,1766,1767,1771,1775],{"path":1759,"title":1760,"description":1761,"category":10,"score":252},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fbase\u002Fvar","全局对象与变量","Node.js 提供模块内可直接使用的全局对象；与浏览器 window 不同，模块顶层的 const\u002Flet 不会挂到 global。见 后端入门概览、模块系统。",{"path":1763,"title":1764,"description":1765,"category":10,"score":252},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fbase\u002Fmodule","模块系统","Node.js 的模块系统是其核心设计之一，它允许开发者将代码拆分为多个文件（模块），通过 require（CommonJS）或 import\u002Fexport（ESM）进行组织和复用。下面详细介绍 CommonJS、ESM 以及两者的互操作。",{"path":7,"title":8,"description":9,"category":10,"score":252},{"path":1768,"title":1769,"description":1770,"category":10,"score":178},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fbasics","后端入门概览","本目录覆盖 JavaScript\u002FTypeScript 服务端运行时与框架、数据存储，以及 Rust 系统编程入门，提供从零到部署的完整学习路径。前置建议：JavaScript 基础、工程化 · 环境变量。",{"path":1772,"title":1773,"description":1774,"category":10,"score":178},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fcore\u002Ffs","文件系统","Node.js 的 fs 模块提供了与文件系统交互的 API，几乎涵盖了所有标准文件操作。它支持三种风格的 API：同步、回调式异步 和 Promise 式异步。下面详细介绍这些 API 的选择策略、流式读写、文件监视以及常用操作。",{"path":1776,"title":1777,"description":1778,"category":10,"score":178},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fasync","异步编程与模式","Node.js 的核心优势在于异步非阻塞 I\u002FO，但这也带来了回调地狱、错误处理复杂等问题。下面从解救方案、工具函数、并发控制、异步迭代以及常见反模式等角度展开。",{"series":1780,"title":1781,"items":1782,"index":1790,"prev":1791,"next":1792},"booklet\u002Fend\u002Fnodejs\u002Fbase","base",[1783,1784,1786,1788],{"path":7,"title":8,"date":14,"pinned":15},{"path":1650,"title":1454,"date":1785,"pinned":15},"2024-11-20",{"path":1763,"title":1764,"date":1787,"pinned":15},"2020-07-01",{"path":1759,"title":1760,"date":1789,"pinned":15},"2025-01-03",0,null,{"path":1650,"title":1454,"date":1785,"pinned":15}]