如何在Monaco Editor中对InlineCompletionProvider调用防抖节流?
Monaco Editor 自定义InlineCompletionsProvider 防抖节流方案
针对你遇到的每次按键触发冗余请求、手动防抖导致卡顿的问题,可以结合Monaco内置的取消机制和针对性的防抖逻辑来解决,以下是具体策略:
1. 结合CancellationToken实现智能防抖
Monaco的provideInlineCompletions方法会传入CancellationToken,这个token会在用户输入新内容时触发取消信号。我们可以利用它替代单纯的lodash防抖,避免卡顿:
- 维护当前的定时器和请求实例,每次新请求进来时先取消之前的任务
- 设置短延迟(比如200ms),延迟期间如果用户继续输入,通过token终止等待
- 请求过程中监听token,一旦用户输入新内容就终止API请求
代码实现示例:
class MonacoCompletionProvider { #currentTimer = null; #currentRequest = null; async provideInlineCompletions(model, position, context, token) { // 清理之前的定时器和未完成请求 if (this.#currentTimer) { clearTimeout(this.#currentTimer); this.#currentTimer = null; } if (this.#currentRequest) { this.#currentRequest.abort(); this.#currentRequest = null; } // 等待延迟,期间监听取消信号 await new Promise(resolve => { this.#currentTimer = setTimeout(resolve, 200); token.onCancellationRequested(() => { clearTimeout(this.#currentTimer); this.#currentTimer = null; resolve(); }); }); // 已被取消则直接返回空 if (token.isCancellationRequested) { return { items: [] }; } try { const controller = new AbortController(); this.#currentRequest = controller; // 绑定token取消信号到请求终止 token.onCancellationRequested(() => { controller.abort(); this.#currentRequest = null; }); // 调用OpenAI API(替换为你的实际请求逻辑) const response = await fetch('https://api.openai.com/v1/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_API_KEY' }, body: JSON.stringify({ model: 'text-davinci-003', prompt: model.getValue().substring(0, model.getOffsetAt(position)), max_tokens: 50 }), signal: controller.signal }); const data = await response.json(); this.#currentRequest = null; // 转换为Monaco兼容的返回格式 return { items: [ { insertText: data.choices[0].text.trim(), range: new monaco.Range( position.lineNumber, position.column, position.lineNumber, position.column ) } ] }; } catch (err) { if (err.name !== 'AbortError') { console.error('API请求失败:', err); } this.#currentRequest = null; return { items: [] }; } } }
2. 过滤无效触发场景
不是所有按键都需要触发API请求,可以提前过滤掉无意义的输入:
- 忽略退格键、方向键、Tab、回车等非内容输入键
- 仅在输入有效字符(字母、数字、代码符号)时才考虑触发请求
- 连续输入空格等无意义内容时直接返回空结果
示例代码:
provideInlineCompletions(model, position, context, token) { // 过滤无效触发字符 const invalidTriggers = ['Backspace', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Tab', 'Enter']; if (context.triggerCharacter && invalidTriggers.includes(context.triggerCharacter)) { return { items: [] }; } // 检查当前输入是否为连续空格(可选) const currentLine = model.getLineContent(position.lineNumber); const currentPrefix = currentLine.substring(0, position.column); if (/^\s*$/.test(currentPrefix)) { return { items: [] }; } // 后续防抖和请求逻辑... }
3. 缓存重复请求结果
对于相同的代码上下文(比如相同的代码前缀),可以缓存API返回结果,避免重复请求:
- 用代码前缀作为缓存key,设置合理的过期时间
- 每次请求前先检查缓存,存在有效缓存则直接返回
示例:
class MonacoCompletionProvider { #cache = new Map(); #cacheTTL = 5000; // 缓存5秒 // ...其他属性和方法 async provideInlineCompletions(model, position, context, token) { // 生成缓存key const prefix = model.getValue().substring(0, model.getOffsetAt(position)); const cacheEntry = this.#cache.get(prefix); // 检查缓存是否有效 if (cacheEntry && Date.now() - cacheEntry.timestamp < this.#cacheTTL) { return cacheEntry.data; } // ...后续防抖和请求逻辑 // 请求成功后更新缓存 const result = { items: [/* 生成的建议项 */] }; this.#cache.set(prefix, { data: result, timestamp: Date.now() }); return result; } }
内容的提问来源于stack exchange,提问作者Nicolás Fantone
相关产品推荐
相关产品推荐

