[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fasync":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fasync":2402,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fasync":2428},{"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":2398,"stem":2399,"id":2400,"extension":2401},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fasync","异步编程与模式","Node.js 的核心优势在于异步非阻塞 I\u002FO，但这也带来了回调地狱、错误处理复杂等问题。下面从解救方案、工具函数、并发控制、异步迭代以及常见反模式等角度展开。","技能小册",[12,13],"nodejs","async","2025-08-20",false,{"type":17,"value":18,"toc":2386},"minimark",[19,22,27,30,35,40,43,259,263,266,517,521,524,689,693,696,956,959,967,977,1126,1133,1209,1213,1216,1224,1382,1390,1531,1535,1746,1754,1760,1935,1938,2133,2136,2140,2144,2169,2173,2185,2189,2205,2216,2226,2233,2274,2286,2290,2308,2311,2379,2382],[20,21,8],"h1",{"id":8},[23,24,26],"h2",{"id":25},"nodejs-异步编程与模式","Node.js 异步编程与模式",[28,29,9],"p",{},[31,32,34],"h3",{"id":33},"一回调地狱与解救promiseasyncawaiteventemitter发布-订阅","一、回调地狱与解救：Promise、async\u002Fawait、EventEmitter、发布-订阅",[36,37,39],"h4",{"id":38},"_1-回调地狱callback-hell","1. 回调地狱（Callback Hell）",[28,41,42],{},"多层嵌套回调导致代码难以阅读和维护。",[44,45,50],"pre",{"className":46,"code":47,"language":48,"meta":49,"style":49},"language-js shiki shiki-themes material-theme-lighter github-light github-dark","fs.readFile(\"a.txt\", (err, dataA) => {\n    if (err) throw err;\n    fs.readFile(\"b.txt\", (err, dataB) => {\n        if (err) throw err;\n        fs.readFile(\"c.txt\", (err, dataC) => {\n            \u002F\u002F 继续嵌套...\n        });\n    });\n});\n","js","",[51,52,53,108,132,168,186,222,229,239,249],"code",{"__ignoreMap":49},[54,55,58,62,66,70,73,77,81,83,86,89,93,95,98,101,105],"span",{"class":56,"line":57},"line",1,[54,59,61],{"class":60},"su5hD","fs",[54,63,65],{"class":64},"sP7_E",".",[54,67,69],{"class":68},"sGLFI","readFile",[54,71,72],{"class":60},"(",[54,74,76],{"class":75},"sjJ54","\"",[54,78,80],{"class":79},"s_sjI","a.txt",[54,82,76],{"class":75},[54,84,85],{"class":64},",",[54,87,88],{"class":64}," (",[54,90,92],{"class":91},"s99_P","err",[54,94,85],{"class":64},[54,96,97],{"class":91}," dataA",[54,99,100],{"class":64},")",[54,102,104],{"class":103},"sbsja"," =>",[54,106,107],{"class":64}," {\n",[54,109,111,115,118,120,123,126,129],{"class":56,"line":110},2,[54,112,114],{"class":113},"sVHd0","    if",[54,116,88],{"class":117},"skxfh",[54,119,92],{"class":60},[54,121,122],{"class":117},") ",[54,124,125],{"class":113},"throw",[54,127,128],{"class":60}," err",[54,130,131],{"class":64},";\n",[54,133,135,138,140,142,144,146,149,151,153,155,157,159,162,164,166],{"class":56,"line":134},3,[54,136,137],{"class":60},"    fs",[54,139,65],{"class":64},[54,141,69],{"class":68},[54,143,72],{"class":117},[54,145,76],{"class":75},[54,147,148],{"class":79},"b.txt",[54,150,76],{"class":75},[54,152,85],{"class":64},[54,154,88],{"class":64},[54,156,92],{"class":91},[54,158,85],{"class":64},[54,160,161],{"class":91}," dataB",[54,163,100],{"class":64},[54,165,104],{"class":103},[54,167,107],{"class":64},[54,169,171,174,176,178,180,182,184],{"class":56,"line":170},4,[54,172,173],{"class":113},"        if",[54,175,88],{"class":117},[54,177,92],{"class":60},[54,179,122],{"class":117},[54,181,125],{"class":113},[54,183,128],{"class":60},[54,185,131],{"class":64},[54,187,189,192,194,196,198,200,203,205,207,209,211,213,216,218,220],{"class":56,"line":188},5,[54,190,191],{"class":60},"        fs",[54,193,65],{"class":64},[54,195,69],{"class":68},[54,197,72],{"class":117},[54,199,76],{"class":75},[54,201,202],{"class":79},"c.txt",[54,204,76],{"class":75},[54,206,85],{"class":64},[54,208,88],{"class":64},[54,210,92],{"class":91},[54,212,85],{"class":64},[54,214,215],{"class":91}," dataC",[54,217,100],{"class":64},[54,219,104],{"class":103},[54,221,107],{"class":64},[54,223,225],{"class":56,"line":224},6,[54,226,228],{"class":227},"sutJx","            \u002F\u002F 继续嵌套...\n",[54,230,232,235,237],{"class":56,"line":231},7,[54,233,234],{"class":64},"        }",[54,236,100],{"class":117},[54,238,131],{"class":64},[54,240,242,245,247],{"class":56,"line":241},8,[54,243,244],{"class":64},"    }",[54,246,100],{"class":117},[54,248,131],{"class":64},[54,250,252,255,257],{"class":56,"line":251},9,[54,253,254],{"class":64},"}",[54,256,100],{"class":60},[54,258,131],{"class":64},[36,260,262],{"id":261},"_2-promise","2. Promise",[28,264,265],{},"将异步操作封装为 Promise 链，避免嵌套。",[44,267,269],{"className":46,"code":268,"language":48,"meta":49,"style":49},"const readFilePromise = file =>\n    new Promise((resolve, reject) => {\n        fs.readFile(file, (err, data) => (err ? reject(err) : resolve(data)));\n    });\n\nreadFilePromise(\"a.txt\")\n    .then(dataA => readFilePromise(\"b.txt\"))\n    .then(dataB => readFilePromise(\"c.txt\"))\n    .then(dataC => console.log(\"done\"))\n    .catch(err => console.error(err));\n",[51,270,271,290,317,376,384,390,406,434,459,491],{"__ignoreMap":49},[54,272,273,276,280,284,287],{"class":56,"line":57},[54,274,275],{"class":103},"const",[54,277,279],{"class":278},"sfCm-"," readFilePromise",[54,281,283],{"class":282},"smGrS"," =",[54,285,286],{"class":91}," file",[54,288,289],{"class":103}," =>\n",[54,291,292,295,299,301,303,306,308,311,313,315],{"class":56,"line":110},[54,293,294],{"class":282},"    new",[54,296,298],{"class":297},"sZMiF"," Promise",[54,300,72],{"class":60},[54,302,72],{"class":64},[54,304,305],{"class":91},"resolve",[54,307,85],{"class":64},[54,309,310],{"class":91}," reject",[54,312,100],{"class":64},[54,314,104],{"class":103},[54,316,107],{"class":64},[54,318,319,321,323,325,327,330,332,334,336,338,341,343,345,347,349,352,354,356,358,360,363,366,368,371,374],{"class":56,"line":134},[54,320,191],{"class":60},[54,322,65],{"class":64},[54,324,69],{"class":68},[54,326,72],{"class":117},[54,328,329],{"class":60},"file",[54,331,85],{"class":64},[54,333,88],{"class":64},[54,335,92],{"class":91},[54,337,85],{"class":64},[54,339,340],{"class":91}," data",[54,342,100],{"class":64},[54,344,104],{"class":103},[54,346,88],{"class":117},[54,348,92],{"class":60},[54,350,351],{"class":282}," ?",[54,353,310],{"class":68},[54,355,72],{"class":117},[54,357,92],{"class":60},[54,359,122],{"class":117},[54,361,362],{"class":282},":",[54,364,365],{"class":68}," resolve",[54,367,72],{"class":117},[54,369,370],{"class":60},"data",[54,372,373],{"class":117},")))",[54,375,131],{"class":64},[54,377,378,380,382],{"class":56,"line":170},[54,379,244],{"class":64},[54,381,100],{"class":60},[54,383,131],{"class":64},[54,385,386],{"class":56,"line":188},[54,387,389],{"emptyLinePlaceholder":388},true,"\n",[54,391,392,395,397,399,401,403],{"class":56,"line":224},[54,393,394],{"class":68},"readFilePromise",[54,396,72],{"class":60},[54,398,76],{"class":75},[54,400,80],{"class":79},[54,402,76],{"class":75},[54,404,405],{"class":60},")\n",[54,407,408,411,414,416,419,421,423,425,427,429,431],{"class":56,"line":231},[54,409,410],{"class":64},"    .",[54,412,413],{"class":68},"then",[54,415,72],{"class":60},[54,417,418],{"class":91},"dataA",[54,420,104],{"class":103},[54,422,279],{"class":68},[54,424,72],{"class":60},[54,426,76],{"class":75},[54,428,148],{"class":79},[54,430,76],{"class":75},[54,432,433],{"class":60},"))\n",[54,435,436,438,440,442,445,447,449,451,453,455,457],{"class":56,"line":241},[54,437,410],{"class":64},[54,439,413],{"class":68},[54,441,72],{"class":60},[54,443,444],{"class":91},"dataB",[54,446,104],{"class":103},[54,448,279],{"class":68},[54,450,72],{"class":60},[54,452,76],{"class":75},[54,454,202],{"class":79},[54,456,76],{"class":75},[54,458,433],{"class":60},[54,460,461,463,465,467,470,472,475,477,480,482,484,487,489],{"class":56,"line":251},[54,462,410],{"class":64},[54,464,413],{"class":68},[54,466,72],{"class":60},[54,468,469],{"class":91},"dataC",[54,471,104],{"class":103},[54,473,474],{"class":60}," console",[54,476,65],{"class":64},[54,478,479],{"class":68},"log",[54,481,72],{"class":60},[54,483,76],{"class":75},[54,485,486],{"class":79},"done",[54,488,76],{"class":75},[54,490,433],{"class":60},[54,492,494,496,499,501,503,505,507,509,512,515],{"class":56,"line":493},10,[54,495,410],{"class":64},[54,497,498],{"class":68},"catch",[54,500,72],{"class":60},[54,502,92],{"class":91},[54,504,104],{"class":103},[54,506,474],{"class":60},[54,508,65],{"class":64},[54,510,511],{"class":68},"error",[54,513,514],{"class":60},"(err))",[54,516,131],{"class":64},[36,518,520],{"id":519},"_3-asyncawait","3. async\u002Fawait",[28,522,523],{},"基于 Promise 的语法糖，使异步代码像同步一样线性书写。",[44,525,527],{"className":46,"code":526,"language":48,"meta":49,"style":49},"async function processFiles() {\n    try {\n        const dataA = await readFilePromise(\"a.txt\");\n        const dataB = await readFilePromise(\"b.txt\");\n        const dataC = await readFilePromise(\"c.txt\");\n        console.log(\"done\");\n    } catch (err) {\n        console.error(err);\n    }\n}\n",[51,528,529,544,551,578,602,626,647,663,679,684],{"__ignoreMap":49},[54,530,531,533,536,539,542],{"class":56,"line":57},[54,532,13],{"class":103},[54,534,535],{"class":103}," function",[54,537,538],{"class":68}," processFiles",[54,540,541],{"class":64},"()",[54,543,107],{"class":64},[54,545,546,549],{"class":56,"line":110},[54,547,548],{"class":113},"    try",[54,550,107],{"class":64},[54,552,553,556,559,561,564,566,568,570,572,574,576],{"class":56,"line":134},[54,554,555],{"class":103},"        const",[54,557,97],{"class":558},"s_hVV",[54,560,283],{"class":282},[54,562,563],{"class":113}," await",[54,565,279],{"class":68},[54,567,72],{"class":117},[54,569,76],{"class":75},[54,571,80],{"class":79},[54,573,76],{"class":75},[54,575,100],{"class":117},[54,577,131],{"class":64},[54,579,580,582,584,586,588,590,592,594,596,598,600],{"class":56,"line":170},[54,581,555],{"class":103},[54,583,161],{"class":558},[54,585,283],{"class":282},[54,587,563],{"class":113},[54,589,279],{"class":68},[54,591,72],{"class":117},[54,593,76],{"class":75},[54,595,148],{"class":79},[54,597,76],{"class":75},[54,599,100],{"class":117},[54,601,131],{"class":64},[54,603,604,606,608,610,612,614,616,618,620,622,624],{"class":56,"line":188},[54,605,555],{"class":103},[54,607,215],{"class":558},[54,609,283],{"class":282},[54,611,563],{"class":113},[54,613,279],{"class":68},[54,615,72],{"class":117},[54,617,76],{"class":75},[54,619,202],{"class":79},[54,621,76],{"class":75},[54,623,100],{"class":117},[54,625,131],{"class":64},[54,627,628,631,633,635,637,639,641,643,645],{"class":56,"line":224},[54,629,630],{"class":60},"        console",[54,632,65],{"class":64},[54,634,479],{"class":68},[54,636,72],{"class":117},[54,638,76],{"class":75},[54,640,486],{"class":79},[54,642,76],{"class":75},[54,644,100],{"class":117},[54,646,131],{"class":64},[54,648,649,651,654,656,658,660],{"class":56,"line":231},[54,650,244],{"class":64},[54,652,653],{"class":113}," catch",[54,655,88],{"class":117},[54,657,92],{"class":60},[54,659,122],{"class":117},[54,661,662],{"class":64},"{\n",[54,664,665,667,669,671,673,675,677],{"class":56,"line":241},[54,666,630],{"class":60},[54,668,65],{"class":64},[54,670,511],{"class":68},[54,672,72],{"class":117},[54,674,92],{"class":60},[54,676,100],{"class":117},[54,678,131],{"class":64},[54,680,681],{"class":56,"line":251},[54,682,683],{"class":64},"    }\n",[54,685,686],{"class":56,"line":493},[54,687,688],{"class":64},"}\n",[36,690,692],{"id":691},"_4-eventemitter-与发布-订阅模式","4. EventEmitter 与发布-订阅模式",[28,694,695],{},"适用于多个异步事件触发多次响应的场景（如流数据、用户交互）。",[44,697,699],{"className":46,"code":698,"language":48,"meta":49,"style":49},"const EventEmitter = require(\"events\");\nconst ee = new EventEmitter();\n\nee.on(\"data\", chunk => {\n    console.log(\"收到数据:\", chunk);\n});\nee.on(\"end\", () => {\n    console.log(\"完成\");\n});\n\n\u002F\u002F 异步过程\nsetTimeout(() => {\n    ee.emit(\"data\", \"hello\");\n    ee.emit(\"end\");\n}, 100);\n",[51,700,701,726,744,748,775,801,809,835,856,864,868,874,888,921,942],{"__ignoreMap":49},[54,702,703,705,708,710,713,715,717,720,722,724],{"class":56,"line":57},[54,704,275],{"class":103},[54,706,707],{"class":558}," EventEmitter",[54,709,283],{"class":282},[54,711,712],{"class":68}," require",[54,714,72],{"class":60},[54,716,76],{"class":75},[54,718,719],{"class":79},"events",[54,721,76],{"class":75},[54,723,100],{"class":60},[54,725,131],{"class":64},[54,727,728,730,733,735,738,740,742],{"class":56,"line":110},[54,729,275],{"class":103},[54,731,732],{"class":558}," ee",[54,734,283],{"class":282},[54,736,737],{"class":282}," new",[54,739,707],{"class":68},[54,741,541],{"class":60},[54,743,131],{"class":64},[54,745,746],{"class":56,"line":134},[54,747,389],{"emptyLinePlaceholder":388},[54,749,750,753,755,758,760,762,764,766,768,771,773],{"class":56,"line":170},[54,751,752],{"class":60},"ee",[54,754,65],{"class":64},[54,756,757],{"class":68},"on",[54,759,72],{"class":60},[54,761,76],{"class":75},[54,763,370],{"class":79},[54,765,76],{"class":75},[54,767,85],{"class":64},[54,769,770],{"class":91}," chunk",[54,772,104],{"class":103},[54,774,107],{"class":64},[54,776,777,780,782,784,786,788,791,793,795,797,799],{"class":56,"line":188},[54,778,779],{"class":60},"    console",[54,781,65],{"class":64},[54,783,479],{"class":68},[54,785,72],{"class":117},[54,787,76],{"class":75},[54,789,790],{"class":79},"收到数据:",[54,792,76],{"class":75},[54,794,85],{"class":64},[54,796,770],{"class":60},[54,798,100],{"class":117},[54,800,131],{"class":64},[54,802,803,805,807],{"class":56,"line":224},[54,804,254],{"class":64},[54,806,100],{"class":60},[54,808,131],{"class":64},[54,810,811,813,815,817,819,821,824,826,828,831,833],{"class":56,"line":231},[54,812,752],{"class":60},[54,814,65],{"class":64},[54,816,757],{"class":68},[54,818,72],{"class":60},[54,820,76],{"class":75},[54,822,823],{"class":79},"end",[54,825,76],{"class":75},[54,827,85],{"class":64},[54,829,830],{"class":64}," ()",[54,832,104],{"class":103},[54,834,107],{"class":64},[54,836,837,839,841,843,845,847,850,852,854],{"class":56,"line":241},[54,838,779],{"class":60},[54,840,65],{"class":64},[54,842,479],{"class":68},[54,844,72],{"class":117},[54,846,76],{"class":75},[54,848,849],{"class":79},"完成",[54,851,76],{"class":75},[54,853,100],{"class":117},[54,855,131],{"class":64},[54,857,858,860,862],{"class":56,"line":251},[54,859,254],{"class":64},[54,861,100],{"class":60},[54,863,131],{"class":64},[54,865,866],{"class":56,"line":493},[54,867,389],{"emptyLinePlaceholder":388},[54,869,871],{"class":56,"line":870},11,[54,872,873],{"class":227},"\u002F\u002F 异步过程\n",[54,875,877,880,882,884,886],{"class":56,"line":876},12,[54,878,879],{"class":68},"setTimeout",[54,881,72],{"class":60},[54,883,541],{"class":64},[54,885,104],{"class":103},[54,887,107],{"class":64},[54,889,891,894,896,899,901,903,905,907,909,912,915,917,919],{"class":56,"line":890},13,[54,892,893],{"class":60},"    ee",[54,895,65],{"class":64},[54,897,898],{"class":68},"emit",[54,900,72],{"class":117},[54,902,76],{"class":75},[54,904,370],{"class":79},[54,906,76],{"class":75},[54,908,85],{"class":64},[54,910,911],{"class":75}," \"",[54,913,914],{"class":79},"hello",[54,916,76],{"class":75},[54,918,100],{"class":117},[54,920,131],{"class":64},[54,922,924,926,928,930,932,934,936,938,940],{"class":56,"line":923},14,[54,925,893],{"class":60},[54,927,65],{"class":64},[54,929,898],{"class":68},[54,931,72],{"class":117},[54,933,76],{"class":75},[54,935,823],{"class":79},[54,937,76],{"class":75},[54,939,100],{"class":117},[54,941,131],{"class":64},[54,943,945,948,952,954],{"class":56,"line":944},15,[54,946,947],{"class":64},"},",[54,949,951],{"class":950},"srdBf"," 100",[54,953,100],{"class":60},[54,955,131],{"class":64},[28,957,958],{},"发布-订阅（如 Redis Pub\u002FSub）可以跨进程、跨服务器解耦。",[31,960,962,963,966],{"id":961},"二utilpromisify-将回调函数转为-promise","二、",[51,964,965],{},"util.promisify"," 将回调函数转为 Promise",[28,968,969,970,973,974,976],{},"Node.js 内置模块大多采用错误优先回调风格（",[51,971,972],{},"fn(err, result)","）。",[51,975,965],{}," 将这些函数转换为返回 Promise 的版本。",[44,978,980],{"className":46,"code":979,"language":48,"meta":49,"style":49},"const util = require(\"util\");\nconst fs = require(\"fs\");\n\nconst readFile = util.promisify(fs.readFile);\nreadFile(\"file.txt\", \"utf8\")\n    .then(data => console.log(data))\n    .catch(err => console.error(err));\n",[51,981,982,1006,1029,1033,1059,1083,1104],{"__ignoreMap":49},[54,983,984,986,989,991,993,995,997,1000,1002,1004],{"class":56,"line":57},[54,985,275],{"class":103},[54,987,988],{"class":558}," util",[54,990,283],{"class":282},[54,992,712],{"class":68},[54,994,72],{"class":60},[54,996,76],{"class":75},[54,998,999],{"class":79},"util",[54,1001,76],{"class":75},[54,1003,100],{"class":60},[54,1005,131],{"class":64},[54,1007,1008,1010,1013,1015,1017,1019,1021,1023,1025,1027],{"class":56,"line":110},[54,1009,275],{"class":103},[54,1011,1012],{"class":558}," fs",[54,1014,283],{"class":282},[54,1016,712],{"class":68},[54,1018,72],{"class":60},[54,1020,76],{"class":75},[54,1022,61],{"class":79},[54,1024,76],{"class":75},[54,1026,100],{"class":60},[54,1028,131],{"class":64},[54,1030,1031],{"class":56,"line":134},[54,1032,389],{"emptyLinePlaceholder":388},[54,1034,1035,1037,1040,1042,1044,1046,1049,1052,1054,1057],{"class":56,"line":170},[54,1036,275],{"class":103},[54,1038,1039],{"class":558}," readFile",[54,1041,283],{"class":282},[54,1043,988],{"class":60},[54,1045,65],{"class":64},[54,1047,1048],{"class":68},"promisify",[54,1050,1051],{"class":60},"(fs",[54,1053,65],{"class":64},[54,1055,1056],{"class":60},"readFile)",[54,1058,131],{"class":64},[54,1060,1061,1063,1065,1067,1070,1072,1074,1076,1079,1081],{"class":56,"line":188},[54,1062,69],{"class":68},[54,1064,72],{"class":60},[54,1066,76],{"class":75},[54,1068,1069],{"class":79},"file.txt",[54,1071,76],{"class":75},[54,1073,85],{"class":64},[54,1075,911],{"class":75},[54,1077,1078],{"class":79},"utf8",[54,1080,76],{"class":75},[54,1082,405],{"class":60},[54,1084,1085,1087,1089,1091,1093,1095,1097,1099,1101],{"class":56,"line":224},[54,1086,410],{"class":64},[54,1088,413],{"class":68},[54,1090,72],{"class":60},[54,1092,370],{"class":91},[54,1094,104],{"class":103},[54,1096,474],{"class":60},[54,1098,65],{"class":64},[54,1100,479],{"class":68},[54,1102,1103],{"class":60},"(data))\n",[54,1105,1106,1108,1110,1112,1114,1116,1118,1120,1122,1124],{"class":56,"line":231},[54,1107,410],{"class":64},[54,1109,498],{"class":68},[54,1111,72],{"class":60},[54,1113,92],{"class":91},[54,1115,104],{"class":103},[54,1117,474],{"class":60},[54,1119,65],{"class":64},[54,1121,511],{"class":68},[54,1123,514],{"class":60},[54,1125,131],{"class":64},[28,1127,1128,1129,1132],{},"也适用于自定义函数，只要遵循 ",[51,1130,1131],{},"(err, res)"," 签名：",[44,1134,1136],{"className":46,"code":1135,"language":48,"meta":49,"style":49},"function customApi(arg, callback) {\n    \u002F\u002F some async work\n    callback(null, result);\n}\nconst customPromise = util.promisify(customApi);\n",[51,1137,1138,1160,1165,1185,1189],{"__ignoreMap":49},[54,1139,1140,1143,1146,1148,1151,1153,1156,1158],{"class":56,"line":57},[54,1141,1142],{"class":103},"function",[54,1144,1145],{"class":68}," customApi",[54,1147,72],{"class":64},[54,1149,1150],{"class":91},"arg",[54,1152,85],{"class":64},[54,1154,1155],{"class":91}," callback",[54,1157,100],{"class":64},[54,1159,107],{"class":64},[54,1161,1162],{"class":56,"line":110},[54,1163,1164],{"class":227},"    \u002F\u002F some async work\n",[54,1166,1167,1170,1172,1176,1178,1181,1183],{"class":56,"line":134},[54,1168,1169],{"class":68},"    callback",[54,1171,72],{"class":117},[54,1173,1175],{"class":1174},"s39Yj","null",[54,1177,85],{"class":64},[54,1179,1180],{"class":60}," result",[54,1182,100],{"class":117},[54,1184,131],{"class":64},[54,1186,1187],{"class":56,"line":170},[54,1188,688],{"class":64},[54,1190,1191,1193,1196,1198,1200,1202,1204,1207],{"class":56,"line":188},[54,1192,275],{"class":103},[54,1194,1195],{"class":558}," customPromise",[54,1197,283],{"class":282},[54,1199,988],{"class":60},[54,1201,65],{"class":64},[54,1203,1048],{"class":68},[54,1205,1206],{"class":60},"(customApi)",[54,1208,131],{"class":64},[31,1210,1212],{"id":1211},"三控制并发","三、控制并发",[28,1214,1215],{},"大量异步操作（如批量 API 调用、文件读取）若不加限制，可能耗尽系统资源或触发速率限制。常用库和模式：",[36,1217,1219,1220,1223],{"id":1218},"_1-p-limit轻量级-promise-并发控制","1. ",[51,1221,1222],{},"p-limit","（轻量级 Promise 并发控制）",[44,1225,1227],{"className":46,"code":1226,"language":48,"meta":49,"style":49},"const pLimit = require('p-limit');\nconst limit = pLimit(5); \u002F\u002F 最大并发 5\n\nconst urls = ['url1', 'url2', ...];\nconst requests = urls.map(url => limit(() => fetch(url)));\nconst results = await Promise.all(requests);\n",[51,1228,1229,1253,1277,1281,1320,1359],{"__ignoreMap":49},[54,1230,1231,1233,1236,1238,1240,1242,1245,1247,1249,1251],{"class":56,"line":57},[54,1232,275],{"class":103},[54,1234,1235],{"class":558}," pLimit",[54,1237,283],{"class":282},[54,1239,712],{"class":68},[54,1241,72],{"class":60},[54,1243,1244],{"class":75},"'",[54,1246,1222],{"class":79},[54,1248,1244],{"class":75},[54,1250,100],{"class":60},[54,1252,131],{"class":64},[54,1254,1255,1257,1260,1262,1264,1266,1269,1271,1274],{"class":56,"line":110},[54,1256,275],{"class":103},[54,1258,1259],{"class":558}," limit",[54,1261,283],{"class":282},[54,1263,1235],{"class":68},[54,1265,72],{"class":60},[54,1267,1268],{"class":950},"5",[54,1270,100],{"class":60},[54,1272,1273],{"class":64},";",[54,1275,1276],{"class":227}," \u002F\u002F 最大并发 5\n",[54,1278,1279],{"class":56,"line":134},[54,1280,389],{"emptyLinePlaceholder":388},[54,1282,1283,1285,1288,1290,1293,1295,1298,1300,1302,1305,1308,1310,1312,1315,1318],{"class":56,"line":170},[54,1284,275],{"class":103},[54,1286,1287],{"class":558}," urls",[54,1289,283],{"class":282},[54,1291,1292],{"class":60}," [",[54,1294,1244],{"class":75},[54,1296,1297],{"class":79},"url1",[54,1299,1244],{"class":75},[54,1301,85],{"class":64},[54,1303,1304],{"class":75}," '",[54,1306,1307],{"class":79},"url2",[54,1309,1244],{"class":75},[54,1311,85],{"class":64},[54,1313,1314],{"class":282}," ...",[54,1316,1317],{"class":60},"]",[54,1319,131],{"class":64},[54,1321,1322,1324,1327,1329,1331,1333,1336,1338,1341,1343,1345,1347,1349,1351,1354,1357],{"class":56,"line":188},[54,1323,275],{"class":103},[54,1325,1326],{"class":558}," requests",[54,1328,283],{"class":282},[54,1330,1287],{"class":60},[54,1332,65],{"class":64},[54,1334,1335],{"class":68},"map",[54,1337,72],{"class":60},[54,1339,1340],{"class":91},"url",[54,1342,104],{"class":103},[54,1344,1259],{"class":68},[54,1346,72],{"class":60},[54,1348,541],{"class":64},[54,1350,104],{"class":103},[54,1352,1353],{"class":68}," fetch",[54,1355,1356],{"class":60},"(url)))",[54,1358,131],{"class":64},[54,1360,1361,1363,1366,1368,1370,1372,1374,1377,1380],{"class":56,"line":224},[54,1362,275],{"class":103},[54,1364,1365],{"class":558}," results",[54,1367,283],{"class":282},[54,1369,563],{"class":113},[54,1371,298],{"class":297},[54,1373,65],{"class":64},[54,1375,1376],{"class":68},"all",[54,1378,1379],{"class":60},"(requests)",[54,1381,131],{"class":64},[36,1383,1385,1386,1389],{"id":1384},"_2-asyncqueue基于回调的队列","2. ",[51,1387,1388],{},"async.queue","（基于回调的队列）",[44,1391,1393],{"className":46,"code":1392,"language":48,"meta":49,"style":49},"const { queue } = require(\"async\");\nconst q = queue(async task => {\n    await process(task);\n}, 5); \u002F\u002F 并发数\n\nq.push(tasks);\nq.drain(() => console.log(\"全部完成\"));\n",[51,1394,1395,1424,1446,1463,1477,1481,1496],{"__ignoreMap":49},[54,1396,1397,1399,1402,1405,1408,1410,1412,1414,1416,1418,1420,1422],{"class":56,"line":57},[54,1398,275],{"class":103},[54,1400,1401],{"class":64}," {",[54,1403,1404],{"class":558}," queue",[54,1406,1407],{"class":64}," }",[54,1409,283],{"class":282},[54,1411,712],{"class":68},[54,1413,72],{"class":60},[54,1415,76],{"class":75},[54,1417,13],{"class":79},[54,1419,76],{"class":75},[54,1421,100],{"class":60},[54,1423,131],{"class":64},[54,1425,1426,1428,1431,1433,1435,1437,1439,1442,1444],{"class":56,"line":110},[54,1427,275],{"class":103},[54,1429,1430],{"class":558}," q",[54,1432,283],{"class":282},[54,1434,1404],{"class":68},[54,1436,72],{"class":60},[54,1438,13],{"class":103},[54,1440,1441],{"class":91}," task",[54,1443,104],{"class":103},[54,1445,107],{"class":64},[54,1447,1448,1451,1454,1456,1459,1461],{"class":56,"line":134},[54,1449,1450],{"class":113},"    await",[54,1452,1453],{"class":68}," process",[54,1455,72],{"class":117},[54,1457,1458],{"class":60},"task",[54,1460,100],{"class":117},[54,1462,131],{"class":64},[54,1464,1465,1467,1470,1472,1474],{"class":56,"line":170},[54,1466,947],{"class":64},[54,1468,1469],{"class":950}," 5",[54,1471,100],{"class":60},[54,1473,1273],{"class":64},[54,1475,1476],{"class":227}," \u002F\u002F 并发数\n",[54,1478,1479],{"class":56,"line":188},[54,1480,389],{"emptyLinePlaceholder":388},[54,1482,1483,1486,1488,1491,1494],{"class":56,"line":224},[54,1484,1485],{"class":60},"q",[54,1487,65],{"class":64},[54,1489,1490],{"class":68},"push",[54,1492,1493],{"class":60},"(tasks)",[54,1495,131],{"class":64},[54,1497,1498,1500,1502,1505,1507,1509,1511,1513,1515,1517,1519,1521,1524,1526,1529],{"class":56,"line":231},[54,1499,1485],{"class":60},[54,1501,65],{"class":64},[54,1503,1504],{"class":68},"drain",[54,1506,72],{"class":60},[54,1508,541],{"class":64},[54,1510,104],{"class":103},[54,1512,474],{"class":60},[54,1514,65],{"class":64},[54,1516,479],{"class":68},[54,1518,72],{"class":60},[54,1520,76],{"class":75},[54,1522,1523],{"class":79},"全部完成",[54,1525,76],{"class":75},[54,1527,1528],{"class":60},"))",[54,1530,131],{"class":64},[36,1532,1534],{"id":1533},"_3-自定义令牌桶token-bucket实现更精细限流","3. 自定义令牌桶（Token Bucket）实现更精细限流",[44,1536,1538],{"className":46,"code":1537,"language":48,"meta":49,"style":49},"class TokenBucket {\n    constructor(capacity, fillPerSecond) {\n        this.capacity = capacity;\n        this.tokens = capacity;\n        setInterval(() => {\n            this.tokens = Math.min(this.capacity, this.tokens + fillPerSecond);\n        }, 1000);\n    }\n    async take() {\n        while (this.tokens \u003C= 0) await delay(10);\n        this.tokens--;\n    }\n}\n",[51,1539,1540,1551,1570,1586,1601,1614,1660,1672,1676,1689,1725,1738,1742],{"__ignoreMap":49},[54,1541,1542,1545,1549],{"class":56,"line":57},[54,1543,1544],{"class":103},"class",[54,1546,1548],{"class":1547},"sbgvK"," TokenBucket",[54,1550,107],{"class":64},[54,1552,1553,1556,1558,1561,1563,1566,1568],{"class":56,"line":110},[54,1554,1555],{"class":103},"    constructor",[54,1557,72],{"class":64},[54,1559,1560],{"class":91},"capacity",[54,1562,85],{"class":64},[54,1564,1565],{"class":91}," fillPerSecond",[54,1567,100],{"class":64},[54,1569,107],{"class":64},[54,1571,1572,1575,1577,1579,1581,1584],{"class":56,"line":134},[54,1573,1574],{"class":1174},"        this",[54,1576,65],{"class":64},[54,1578,1560],{"class":60},[54,1580,283],{"class":282},[54,1582,1583],{"class":60}," capacity",[54,1585,131],{"class":64},[54,1587,1588,1590,1592,1595,1597,1599],{"class":56,"line":170},[54,1589,1574],{"class":1174},[54,1591,65],{"class":64},[54,1593,1594],{"class":60},"tokens",[54,1596,283],{"class":282},[54,1598,1583],{"class":60},[54,1600,131],{"class":64},[54,1602,1603,1606,1608,1610,1612],{"class":56,"line":188},[54,1604,1605],{"class":68},"        setInterval",[54,1607,72],{"class":117},[54,1609,541],{"class":64},[54,1611,104],{"class":103},[54,1613,107],{"class":64},[54,1615,1616,1619,1621,1623,1625,1628,1630,1633,1635,1638,1640,1642,1644,1647,1649,1651,1654,1656,1658],{"class":56,"line":224},[54,1617,1618],{"class":1174},"            this",[54,1620,65],{"class":64},[54,1622,1594],{"class":60},[54,1624,283],{"class":282},[54,1626,1627],{"class":60}," Math",[54,1629,65],{"class":64},[54,1631,1632],{"class":68},"min",[54,1634,72],{"class":117},[54,1636,1637],{"class":1174},"this",[54,1639,65],{"class":64},[54,1641,1560],{"class":60},[54,1643,85],{"class":64},[54,1645,1646],{"class":1174}," this",[54,1648,65],{"class":64},[54,1650,1594],{"class":60},[54,1652,1653],{"class":282}," +",[54,1655,1565],{"class":60},[54,1657,100],{"class":117},[54,1659,131],{"class":64},[54,1661,1662,1665,1668,1670],{"class":56,"line":231},[54,1663,1664],{"class":64},"        },",[54,1666,1667],{"class":950}," 1000",[54,1669,100],{"class":117},[54,1671,131],{"class":64},[54,1673,1674],{"class":56,"line":241},[54,1675,683],{"class":64},[54,1677,1678,1681,1685,1687],{"class":56,"line":251},[54,1679,1680],{"class":103},"    async",[54,1682,1684],{"class":1683},"sVXei"," take",[54,1686,541],{"class":64},[54,1688,107],{"class":64},[54,1690,1691,1694,1696,1698,1700,1702,1705,1708,1710,1713,1716,1718,1721,1723],{"class":56,"line":493},[54,1692,1693],{"class":113},"        while",[54,1695,88],{"class":117},[54,1697,1637],{"class":1174},[54,1699,65],{"class":64},[54,1701,1594],{"class":60},[54,1703,1704],{"class":282}," \u003C=",[54,1706,1707],{"class":950}," 0",[54,1709,122],{"class":117},[54,1711,1712],{"class":113},"await",[54,1714,1715],{"class":68}," delay",[54,1717,72],{"class":117},[54,1719,1720],{"class":950},"10",[54,1722,100],{"class":117},[54,1724,131],{"class":64},[54,1726,1727,1729,1731,1733,1736],{"class":56,"line":870},[54,1728,1574],{"class":1174},[54,1730,65],{"class":64},[54,1732,1594],{"class":60},[54,1734,1735],{"class":282},"--",[54,1737,131],{"class":64},[54,1739,1740],{"class":56,"line":876},[54,1741,683],{"class":64},[54,1743,1744],{"class":56,"line":890},[54,1745,688],{"class":64},[31,1747,1749,1750,1753],{"id":1748},"四异步迭代器与-for-awaitof-处理流式数据","四、异步迭代器与 ",[51,1751,1752],{},"for await...of"," 处理流式数据",[28,1755,1756,1757,1759],{},"Node.js 可读流（Readable）实现了异步迭代器，可以用 ",[51,1758,1752],{}," 逐块读取数据。",[44,1761,1763],{"className":46,"code":1762,"language":48,"meta":49,"style":49},"const { createReadStream } = require(\"fs\");\nconst stream = createReadStream(\"large.txt\", { encoding: \"utf8\" });\n\n(async () => {\n    for await (const chunk of stream) {\n        console.log(\"收到 chunk:\", chunk.length);\n    }\n    console.log(\"流结束\");\n})();\n",[51,1764,1765,1792,1833,1837,1849,1871,1901,1905,1926],{"__ignoreMap":49},[54,1766,1767,1769,1771,1774,1776,1778,1780,1782,1784,1786,1788,1790],{"class":56,"line":57},[54,1768,275],{"class":103},[54,1770,1401],{"class":64},[54,1772,1773],{"class":558}," createReadStream",[54,1775,1407],{"class":64},[54,1777,283],{"class":282},[54,1779,712],{"class":68},[54,1781,72],{"class":60},[54,1783,76],{"class":75},[54,1785,61],{"class":79},[54,1787,76],{"class":75},[54,1789,100],{"class":60},[54,1791,131],{"class":64},[54,1793,1794,1796,1799,1801,1803,1805,1807,1810,1812,1814,1816,1819,1821,1823,1825,1827,1829,1831],{"class":56,"line":110},[54,1795,275],{"class":103},[54,1797,1798],{"class":558}," stream",[54,1800,283],{"class":282},[54,1802,1773],{"class":68},[54,1804,72],{"class":60},[54,1806,76],{"class":75},[54,1808,1809],{"class":79},"large.txt",[54,1811,76],{"class":75},[54,1813,85],{"class":64},[54,1815,1401],{"class":64},[54,1817,1818],{"class":117}," encoding",[54,1820,362],{"class":64},[54,1822,911],{"class":75},[54,1824,1078],{"class":79},[54,1826,76],{"class":75},[54,1828,1407],{"class":64},[54,1830,100],{"class":60},[54,1832,131],{"class":64},[54,1834,1835],{"class":56,"line":134},[54,1836,389],{"emptyLinePlaceholder":388},[54,1838,1839,1841,1843,1845,1847],{"class":56,"line":170},[54,1840,72],{"class":60},[54,1842,13],{"class":103},[54,1844,830],{"class":64},[54,1846,104],{"class":103},[54,1848,107],{"class":64},[54,1850,1851,1854,1856,1858,1860,1862,1865,1867,1869],{"class":56,"line":188},[54,1852,1853],{"class":113},"    for",[54,1855,563],{"class":113},[54,1857,88],{"class":117},[54,1859,275],{"class":103},[54,1861,770],{"class":558},[54,1863,1864],{"class":282}," of",[54,1866,1798],{"class":60},[54,1868,122],{"class":117},[54,1870,662],{"class":64},[54,1872,1873,1875,1877,1879,1881,1883,1886,1888,1890,1892,1894,1897,1899],{"class":56,"line":224},[54,1874,630],{"class":60},[54,1876,65],{"class":64},[54,1878,479],{"class":68},[54,1880,72],{"class":117},[54,1882,76],{"class":75},[54,1884,1885],{"class":79},"收到 chunk:",[54,1887,76],{"class":75},[54,1889,85],{"class":64},[54,1891,770],{"class":60},[54,1893,65],{"class":64},[54,1895,1896],{"class":558},"length",[54,1898,100],{"class":117},[54,1900,131],{"class":64},[54,1902,1903],{"class":56,"line":231},[54,1904,683],{"class":64},[54,1906,1907,1909,1911,1913,1915,1917,1920,1922,1924],{"class":56,"line":241},[54,1908,779],{"class":60},[54,1910,65],{"class":64},[54,1912,479],{"class":68},[54,1914,72],{"class":117},[54,1916,76],{"class":75},[54,1918,1919],{"class":79},"流结束",[54,1921,76],{"class":75},[54,1923,100],{"class":117},[54,1925,131],{"class":64},[54,1927,1928,1930,1933],{"class":56,"line":251},[54,1929,254],{"class":64},[54,1931,1932],{"class":60},")()",[54,1934,131],{"class":64},[28,1936,1937],{},"自定义异步可迭代对象：",[44,1939,1941],{"className":46,"code":1940,"language":48,"meta":49,"style":49},"const asyncIterable = {\n    [Symbol.asyncIterator]() {\n        let i = 0;\n        return {\n            next() {\n                if (i \u003C 3) return Promise.resolve({ value: i++, done: false });\n                return Promise.resolve({ done: true });\n            },\n        };\n    },\n};\nfor await (const val of asyncIterable) console.log(val);\n",[51,1942,1943,1954,1968,1982,1989,1998,2057,2085,2090,2095,2100,2105],{"__ignoreMap":49},[54,1944,1945,1947,1950,1952],{"class":56,"line":57},[54,1946,275],{"class":103},[54,1948,1949],{"class":558}," asyncIterable",[54,1951,283],{"class":282},[54,1953,107],{"class":64},[54,1955,1956,1959,1961,1964,1966],{"class":56,"line":110},[54,1957,1958],{"class":60},"    [Symbol",[54,1960,65],{"class":64},[54,1962,1963],{"class":60},"asyncIterator]",[54,1965,541],{"class":64},[54,1967,107],{"class":64},[54,1969,1970,1973,1976,1978,1980],{"class":56,"line":134},[54,1971,1972],{"class":103},"        let",[54,1974,1975],{"class":60}," i",[54,1977,283],{"class":282},[54,1979,1707],{"class":950},[54,1981,131],{"class":64},[54,1983,1984,1987],{"class":56,"line":170},[54,1985,1986],{"class":113},"        return",[54,1988,107],{"class":64},[54,1990,1991,1994,1996],{"class":56,"line":188},[54,1992,1993],{"class":1683},"            next",[54,1995,541],{"class":64},[54,1997,107],{"class":64},[54,1999,2000,2003,2005,2008,2011,2014,2016,2019,2021,2023,2025,2027,2030,2033,2035,2037,2040,2042,2045,2047,2051,2053,2055],{"class":56,"line":224},[54,2001,2002],{"class":113},"                if",[54,2004,88],{"class":117},[54,2006,2007],{"class":60},"i",[54,2009,2010],{"class":282}," \u003C",[54,2012,2013],{"class":950}," 3",[54,2015,122],{"class":117},[54,2017,2018],{"class":113},"return",[54,2020,298],{"class":297},[54,2022,65],{"class":64},[54,2024,305],{"class":68},[54,2026,72],{"class":117},[54,2028,2029],{"class":64},"{",[54,2031,2032],{"class":117}," value",[54,2034,362],{"class":64},[54,2036,1975],{"class":60},[54,2038,2039],{"class":282},"++",[54,2041,85],{"class":64},[54,2043,2044],{"class":117}," done",[54,2046,362],{"class":64},[54,2048,2050],{"class":2049},"syTEX"," false",[54,2052,1407],{"class":64},[54,2054,100],{"class":117},[54,2056,131],{"class":64},[54,2058,2059,2062,2064,2066,2068,2070,2072,2074,2076,2079,2081,2083],{"class":56,"line":231},[54,2060,2061],{"class":113},"                return",[54,2063,298],{"class":297},[54,2065,65],{"class":64},[54,2067,305],{"class":68},[54,2069,72],{"class":117},[54,2071,2029],{"class":64},[54,2073,2044],{"class":117},[54,2075,362],{"class":64},[54,2077,2078],{"class":2049}," true",[54,2080,1407],{"class":64},[54,2082,100],{"class":117},[54,2084,131],{"class":64},[54,2086,2087],{"class":56,"line":241},[54,2088,2089],{"class":64},"            },\n",[54,2091,2092],{"class":56,"line":251},[54,2093,2094],{"class":64},"        };\n",[54,2096,2097],{"class":56,"line":493},[54,2098,2099],{"class":64},"    },\n",[54,2101,2102],{"class":56,"line":870},[54,2103,2104],{"class":64},"};\n",[54,2106,2107,2110,2112,2114,2116,2119,2121,2124,2126,2128,2131],{"class":56,"line":876},[54,2108,2109],{"class":113},"for",[54,2111,563],{"class":113},[54,2113,88],{"class":60},[54,2115,275],{"class":103},[54,2117,2118],{"class":558}," val",[54,2120,1864],{"class":282},[54,2122,2123],{"class":60}," asyncIterable) console",[54,2125,65],{"class":64},[54,2127,479],{"class":68},[54,2129,2130],{"class":60},"(val)",[54,2132,131],{"class":64},[28,2134,2135],{},"在数据库游标、HTTP 分块响应等场景非常有用。",[31,2137,2139],{"id":2138},"五常见反模式","五、常见反模式",[36,2141,2143],{"id":2142},"_1-未捕获的异步错误","1. 未捕获的异步错误",[2145,2146,2147,2162],"ul",{},[2148,2149,2150,2151,2154,2155,2158,2159,2161],"li",{},"没有 ",[51,2152,2153],{},".catch"," 的 Promise 或未 ",[51,2156,2157],{},"try\u002Fcatch"," 的 ",[51,2160,1712],{}," 会导致静默失败或进程崩溃。",[2148,2163,2164,2165,2168],{},"解决方案：始终添加捕获，或使用 ",[51,2166,2167],{},"process.on('unhandledRejection', handler)","。",[36,2170,2172],{"id":2171},"_2-无限制并发","2. 无限制并发",[2145,2174,2175,2182],{},[2148,2176,2177,2178,2181],{},"同时发起成千上万个异步操作（如 ",[51,2179,2180],{},"Promise.all"," 大量请求）可能耗尽内存、文件描述符或触发服务端限流。",[2148,2183,2184],{},"解决方案：使用上述并发控制库或限流器。",[36,2186,2188],{"id":2187},"_3-同步方法混用阻塞事件循环","3. 同步方法混用（阻塞事件循环）",[2145,2190,2191,2202],{},[2148,2192,2193,2194,2197,2198,2201],{},"在回调或 Promise 执行体中调用 ",[51,2195,2196],{},"fs.readFileSync","、",[51,2199,2200],{},"while"," 循环或 CPU 密集型同步代码。",[2148,2203,2204],{},"解决方案：使用异步版本或将其剥离到 worker_threads。",[36,2206,2208,2209,2212,2213],{"id":2207},"_4-回调地狱的残余过度使用-promisethen-链而非-asyncawait","4. 回调地狱的残余：过度使用 ",[51,2210,2211],{},"Promise.then"," 链而非 ",[51,2214,2215],{},"async\u002Fawait",[2145,2217,2218,2221],{},[2148,2219,2220],{},"虽然 Promise 解决了嵌套，但长链仍难读。",[2148,2222,2223,2224,2168],{},"解决方案：优先使用 ",[51,2225,2215],{},[36,2227,2229,2230,2232],{"id":2228},"_5-忘记-await-关键字","5. 忘记 ",[51,2231,1712],{}," 关键字",[44,2234,2236],{"className":46,"code":2235,"language":48,"meta":49,"style":49},"async function test() {\n    const result = fetchData(); \u002F\u002F 少了 await，result 是 Promise，不是实际值\n}\n",[51,2237,2238,2251,2270],{"__ignoreMap":49},[54,2239,2240,2242,2244,2247,2249],{"class":56,"line":57},[54,2241,13],{"class":103},[54,2243,535],{"class":103},[54,2245,2246],{"class":68}," test",[54,2248,541],{"class":64},[54,2250,107],{"class":64},[54,2252,2253,2256,2258,2260,2263,2265,2267],{"class":56,"line":110},[54,2254,2255],{"class":103},"    const",[54,2257,1180],{"class":558},[54,2259,283],{"class":282},[54,2261,2262],{"class":68}," fetchData",[54,2264,541],{"class":117},[54,2266,1273],{"class":64},[54,2268,2269],{"class":227}," \u002F\u002F 少了 await，result 是 Promise，不是实际值\n",[54,2271,2272],{"class":56,"line":134},[54,2273,688],{"class":64},[2145,2275,2276],{},[2148,2277,2278,2279,2282,2283,2168],{},"解决方案：启用 ESLint 规则 ",[51,2280,2281],{},"@typescript-eslint\u002Fawait-thenable"," 或 ",[51,2284,2285],{},"require-await",[36,2287,2289],{"id":2288},"_6-事件监听器中的错误未捕获","6. 事件监听器中的错误未捕获",[2145,2291,2292,2302],{},[2148,2293,2294,2295,2298,2299,2301],{},"若 ",[51,2296,2297],{},"EventEmitter"," 触发 ",[51,2300,511],{}," 事件但没有监听，Node.js 会将其视为未捕获异常并可能崩溃。",[2148,2303,2304,2305,2307],{},"解决方案：始终监听 ",[51,2306,511],{}," 事件。",[31,2309,2310],{"id":2310},"总结",[2312,2313,2314,2327],"table",{},[2315,2316,2317],"thead",{},[2318,2319,2320,2324],"tr",{},[2321,2322,2323],"th",{},"模式\u002F工具",[2321,2325,2326],{},"作用",[2328,2329,2330,2339,2348,2361,2371],"tbody",{},[2318,2331,2332,2336],{},[2333,2334,2335],"td",{},"Promise + async\u002Fawait",[2333,2337,2338],{},"避免回调地狱，线性编写异步代码",[2318,2340,2341,2345],{},[2333,2342,2343],{},[51,2344,965],{},[2333,2346,2347],{},"将旧回调函数转为 Promise",[2318,2349,2350,2358],{},[2333,2351,2352,2353,2197,2355,2357],{},"并发控制（",[51,2354,1222],{},[51,2356,1388],{},"、令牌桶）",[2333,2359,2360],{},"限制同时进行的异步操作数量",[2318,2362,2363,2368],{},[2333,2364,2365,2366],{},"异步迭代器 + ",[51,2367,1752],{},[2333,2369,2370],{},"流式处理数据块",[2318,2372,2373,2376],{},[2333,2374,2375],{},"反模式预防",[2333,2377,2378],{},"捕获错误、控制并发、避免同步阻塞",[28,2380,2381],{},"掌握这些模式，可以写出健壮、高性能的 Node.js 异步代码。",[2383,2384,2385],"style",{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .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 .sfCm-, html code.shiki .sfCm-{--shiki-light:#90A4AE;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":49,"searchDepth":110,"depth":110,"links":2387},[2388],{"id":25,"depth":110,"text":26,"children":2389},[2390,2391,2393,2394,2396,2397],{"id":33,"depth":134,"text":34},{"id":961,"depth":134,"text":2392},"二、util.promisify 将回调函数转为 Promise",{"id":1211,"depth":134,"text":1212},{"id":1748,"depth":134,"text":2395},"四、异步迭代器与 for await...of 处理流式数据",{"id":2138,"depth":134,"text":2139},{"id":2310,"depth":134,"text":2310},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fend\u002FNodeJs\u002Fasync","knowledge\u002Fknowledge\u002Fbooklet\u002Fend\u002FNodeJs\u002Fasync.md","md",{"items":2403},[2404,2408,2412,2416,2420,2424],{"path":2405,"title":2406,"description":2407,"category":10,"score":224},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fcore\u002Ffs","文件系统","Node.js 的 fs 模块提供了与文件系统交互的 API，几乎涵盖了所有标准文件操作。它支持三种风格的 API：同步、回调式异步 和 Promise 式异步。下面详细介绍这些 API 的选择策略、流式读写、文件监视以及常用操作。",{"path":2409,"title":2410,"description":2411,"category":10,"score":224},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fcore\u002Fbuffer","缓冲区","Buffer 是 Node.js 全局对象，用于处理二进制数据流（如文件、网络数据）。在 ES6 引入 TypedArray 之前，Buffer 是 Node.js 处理二进制的主要方式；现在 Buffer 实现了 Uint8Arra…",{"path":2413,"title":2414,"description":2415,"category":10,"score":224},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fbase\u002Fvar","全局对象与变量","Node.js 提供模块内可直接使用的全局对象；与浏览器 window 不同，模块顶层的 const\u002Flet 不会挂到 global。见 后端入门概览、模块系统。",{"path":2417,"title":2418,"description":2419,"category":10,"score":224},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fbase\u002Fevent-loop","事件循环","Node.js 的事件循环是其实现非阻塞 I\u002FO 的核心机制。它基于 libuv 库，将各种异步操作（定时器、I\u002FO、setImmediate 等）组织成不同的阶段，每个阶段都有一个先进先出的回调队列。事件循环会按照固定的顺序反复执行…",{"path":2421,"title":2422,"description":2423,"category":10,"score":224},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fcore\u002Fpath","路径处理","path 模块提供了用于处理和转换文件路径的实用工具。它是 Node.js 核心模块，无需安装即可直接使用。由于不同操作系统（Windows、Linux、macOS）的路径分隔符不同（Windows 使用反斜杠 \\，POSIX 使用正…",{"path":2425,"title":2426,"description":2427,"category":10,"score":224},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fcore\u002Fprocess","进程与子进程","Node.js 在单个进程中运行，但通过 process 对象、child_process 模块及 worker_threads 模块提供了对进程的精细控制以及多进程\u002F多线程能力。下面从三个方面展开。",{"series":2429,"title":12,"items":2430,"index":2468,"prev":2469,"next":2470},"booklet\u002Fend\u002Fnodejs",[2431,2432,2434,2438,2442,2444,2446,2450,2454,2458,2460,2462,2466],{"path":7,"title":8,"date":14,"pinned":15},{"path":2409,"title":2410,"date":2433,"pinned":15},"2025-01-30",{"path":2435,"title":2436,"date":2437,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Ferror","错误处理与健壮性","2021-01-24",{"path":2439,"title":2440,"date":2441,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fbase\u002Fevent","事件驱动与非阻塞 I\u002FO 模型","2020-06-20",{"path":2417,"title":2418,"date":2443,"pinned":15},"2024-11-20",{"path":2405,"title":2406,"date":2445,"pinned":15},"2026-06-04",{"path":2447,"title":2448,"date":2449,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fcore\u002Fhttp","网络编程","2023-01-27",{"path":2451,"title":2452,"date":2453,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fbase\u002Fmodule","模块系统","2020-07-01",{"path":2455,"title":2456,"date":2457,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fcore\u002Fother","其他常用模块","2020-03-27",{"path":2421,"title":2422,"date":2459,"pinned":15},"2024-03-14",{"path":2425,"title":2426,"date":2461,"pinned":15},"2023-11-10",{"path":2463,"title":2464,"date":2465,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnodejs\u002Fcore\u002Fstream","流","2022-05-03",{"path":2413,"title":2414,"date":2467,"pinned":15},"2025-01-03",0,null,{"path":2409,"title":2410,"date":2433,"pinned":15}]