JS流式模式輸出函數(shù)模式使用詳解
一、傳統(tǒng)模式:一次性返回完整結(jié)果
我們平時(shí)寫接口大多是這樣:
async function getAnswer(question: string): Promise<string> {
const result = await callAI(question); // 等待完整結(jié)果(可能需要幾秒)
return result; // 一次性返回
}
// 控制器中
const answer = await service.getAnswer(question);
ctx.body = { data: answer };如果開發(fā) ai 相關(guān)的項(xiàng)目
問(wèn)題:用戶必須等待 AI 生成完整個(gè)回答才能看到內(nèi)容。如果 AI 生成需要 10 秒,用戶就白屏等待 10 秒,體驗(yàn)很差。
二、流式模式:邊生成邊返回
我們希望 AI 每生成一個(gè)字,就立刻推送給用戶。但 HTTP 響應(yīng)只能發(fā)送一次,不能先返回一部分再返回另一部分。解決辦法是:
- 不關(guān)閉連接,保持 HTTP 響應(yīng)通道開啟,持續(xù)寫入數(shù)據(jù)塊(SSE 或分塊傳輸)。
- 后端需要一種方式,能夠“每產(chǎn)生一個(gè) token 就調(diào)用一個(gè)函數(shù)”來(lái)寫入數(shù)據(jù)。
這就是 回調(diào)函數(shù)模式 的應(yīng)用場(chǎng)景。
三、回調(diào)函數(shù)模式模擬
想象你有一個(gè)函數(shù) generate,它模擬 AI 逐字產(chǎn)生結(jié)果:
// 模擬 AI 逐字生成
function simulateAI(word: string, callback: (token: string) => void) {
for (const ch of word) {
setTimeout(() => callback(ch), 100); // 每 100ms 輸出一個(gè)字符
}
}如果不用回調(diào),我們可能會(huì)寫:
function generateSync(word: string): string {
let result = '';
for (const ch of word) {
result += ch;
// 無(wú)法在這里把中間結(jié)果返回給調(diào)用方,因?yàn)楹瘮?shù)還沒(méi)結(jié)束
}
return result; // 只能最后一次性返回
}使用回調(diào)后,調(diào)用方可以這樣:
simulateAI('你好', (token) => {
console.log('收到 token:', token); // 會(huì)打印三次:你、好
});
回調(diào)函數(shù) (token) => { ... } 就像是“收到數(shù)據(jù)時(shí)的處理指令”。simulateAI 每產(chǎn)生一個(gè)字,就執(zhí)行這個(gè)指令,把字傳出去。
代碼中的實(shí)際應(yīng)用
BusinessChatService.chatStream 中:
async chatStream(input: string, onToken: (token: string) => void) {
// ... 調(diào)用 LM Studio 流式 API
for await (const chunk of stream) {
// 解析出 token
const token = parsed.choices[0]?.delta?.content;
if (token) {
onToken(token); // 每得到一個(gè) token,就調(diào)用外部傳入的回調(diào)
}
}
}控制器調(diào)用時(shí):
await service.chatStream(input, (token) => {
res.write(`data: ${JSON.stringify({ token })}\n\n`); // 將 token 寫入 HTTP 響應(yīng)
});關(guān)鍵點(diǎn):
chatStream不關(guān)心 token 最終被寫到哪里(可以是 HTTP 響應(yīng)、WebSocket、文件等),它只負(fù)責(zé)“產(chǎn)生 token 時(shí)調(diào)用回調(diào)”。- 控制器負(fù)責(zé)提供“寫入響應(yīng)”的具體實(shí)現(xiàn)(即回調(diào)函數(shù))。
- 這樣實(shí)現(xiàn)了職責(zé)分離:服務(wù)專注于 AI 交互,控制器專注于網(wǎng)絡(luò)傳輸。
- 在傳統(tǒng) CRUD 開發(fā)中,我們通常直接
return數(shù)據(jù)庫(kù)查詢結(jié)果,很少需要“逐行返回”。 - 流式輸出是 AI 應(yīng)用特有的需求,需要改變思維:從“等待完整結(jié)果”到“邊生成邊推送”。
- 回調(diào)函數(shù)通常用于事件驅(qū)動(dòng)(如
addEventListener),但在這里用于異步迭代器的數(shù)據(jù)推送。
六、類比幫助你理解
想象你在廚房做菜(AI 生成回答),服務(wù)員(控制器)站在門口等。
- 傳統(tǒng)模式:你把整盤菜做好后才端出去,顧客餓著肚子等很久。
- 流式模式:你做一道菜(一個(gè)字)就讓服務(wù)員立刻端出去,顧客邊吃邊等。
服務(wù)員怎么知道菜做好了?你每完成一道菜就喊一聲“上菜!”(調(diào)用回調(diào)函數(shù))。服務(wù)員聽到后就把菜端走(寫入 HTTP 響應(yīng))。
在你的代碼中:
- “你” =
chatStream方法 - “上菜!” =
onToken(token) - “服務(wù)員” = 控制器中傳入的箭頭函數(shù)
七、總結(jié)
- 回調(diào)函數(shù)模式 在這里是為了實(shí)現(xiàn)流式輸出,避免用戶等待完整結(jié)果。
chatStream不返回結(jié)果,而是通過(guò)反復(fù)調(diào)用onToken來(lái)“推送”數(shù)據(jù)塊。- 控制器負(fù)責(zé)提供“如何推送”的具體邏輯(寫入 HTTP 響應(yīng))。
- 這種模式對(duì)于 AI 對(duì)話、實(shí)時(shí)數(shù)據(jù)處理非常實(shí)用。
到此這篇關(guān)于JS流式模式輸出函數(shù)模式使用詳解的文章就介紹到這了,更多相關(guān)js流式模式輸出內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- JS創(chuàng)建對(duì)象常用設(shè)計(jì)模式工廠構(gòu)造函數(shù)及原型
- javascript函數(shù)的四種調(diào)用模式
- Javascript 函數(shù)的四種調(diào)用模式
- 淺析JavaScript函數(shù)的調(diào)用模式
- js面向?qū)ο笾R妱?chuàng)建對(duì)象的幾種方式(工廠模式、構(gòu)造函數(shù)模式、原型模式)
- javascript設(shè)計(jì)模式之對(duì)象工廠函數(shù)與構(gòu)造函數(shù)詳解
- javascript組合使用構(gòu)造函數(shù)模式和原型模式實(shí)例
- 深入理解JavaScript系列(26):設(shè)計(jì)模式之構(gòu)造函數(shù)模式詳解
- JavaScript中的函數(shù)模式詳解
- JavaScript函數(shù)模式詳解
相關(guān)文章
JavaScript實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)的三種方法詳解
這篇文章主要介紹了JavaScript實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)的三種主要方法:window.open()、window.location.href和window.location.replace(),包括它們的特點(diǎn)、適用場(chǎng)景及最佳實(shí)踐,需要的朋友可以參考下2026-03-03
JavaScript的原型存在的安全問(wèn)題及解決辦法
JavaScript的原型很多人都知道也很好用,但是很多人在使用原型繼承中導(dǎo)致的安全問(wèn)題卻很少人知道,接下來(lái)我們就來(lái)好好了解一下,感興趣的小伙伴跟著小編一起來(lái)看看吧2023-08-08
JS實(shí)現(xiàn)Date日期格式的本地化的方法小結(jié)
為了更好的更新多語(yǔ)言日期的顯示,所以希望實(shí)現(xiàn)日期的本地化格式顯示要求,常規(guī)的特殊字符型格式化無(wú)法滿足顯示要求,這里整理了幾種我思考實(shí)現(xiàn)的本地化實(shí)現(xiàn)功能2024-06-06
JavaScript三種常用網(wǎng)頁(yè)特效詳解
這篇文章主要介紹了可以用JavaScript實(shí)現(xiàn)的常用的三種網(wǎng)頁(yè)特效:offset系列、client系列、scroll系列。文中示例代碼簡(jiǎn)潔易懂,感興趣的小伙伴可以了解一下2021-12-12
利用OpenAI?ChatGPT進(jìn)行編碼嘗試體驗(yàn)
這幾天chatgpt可是非?;鸬?下面這篇文章主要給大家介紹了關(guān)于利用OpenAI?ChatGPT進(jìn)行編碼嘗試體驗(yàn)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
微信小程序數(shù)據(jù)統(tǒng)計(jì)和錯(cuò)誤統(tǒng)計(jì)的實(shí)現(xiàn)方法
這篇文章主要介紹了微信小程序數(shù)據(jù)統(tǒng)計(jì)和錯(cuò)誤統(tǒng)計(jì)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
在原生不支持的舊環(huán)境中添加兼容的Object.keys實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇在原生不支持的舊環(huán)境中添加兼容的Object.keys實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09
JavaScript操作localStorage實(shí)現(xiàn)保存本地json文件
這篇文章主要為大家詳細(xì)介紹了JavaScript如何操作localStorage實(shí)現(xiàn)保存本地json文件,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02

