
User asks about Shanghai weather tomorrow. Need to call weather tool.
上海明天白天有小雨,夜间转阴,全天温差不大,建议出门带伞。
补充两句:明天紫外线最弱,不用特别防晒;不过空气湿度接近饱和,体感会偏闷黏,衣物不容易干,晚上洗完的衣服建议晾在通风处或直接烘干。后天转为阴天,降雨概率只剩约一成,适合安排户外行程。

用户要求写一个防抖函数(debounce)。这是常见的 JavaScript 工具函数。我应该给出清晰的实现,包含基础版和带 immediate/leading 选项的进阶版,加上 TypeScript 类型和取消功能,并简要说明。
用户之前问天气,现在是技术问题,直接切换即可。
我来写一个完整但不过度冗长的版本。
基础版:
function debounce(fn, delay = 300) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
进阶版:支持 immediate(首次立即执行)、cancel、flush、返回 Promise 或者保留返回值。
我写一个 TypeScript 版本比较完整,再给一个 JS 简化版。
注意 cancel 和 flush 的实现。
interface DebouncedFn<T extends (...args: any[]) => any> {
(...args: Parameters<T>): void;
cancel(): void;
flush(): void;
pending(): boolean;
}
function debounce<T extends (...args: any[]) => any>(
fn: T,
delay = 300,
options: { leading?: boolean; trailing?: boolean } = {}
): DebouncedFn<T> {
const { leading = false, trailing = true } = options;
let timer: ReturnType<typeof setTimeout> | null = null;
let lastArgs: Parameters<T> | null = null;
let lastThis: any = null;
let result: ReturnType<T>;
function invoke() {
if (!lastArgs) return result;
result = fn.apply(lastThis, lastArgs);
lastArgs = lastThis = null;
return result;
}
function debounced(this: any, ...args: Parameters<T>) {
lastArgs = args;
lastThis = this;
const callNow = leading && !timer;
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
if (trailing) invoke();
}, delay);
if (callNow) result = invoke();
return result;
}
debounced.cancel = () => {
if (timer) clearTimeout(timer);
timer = null;
lastArgs = lastThis = null;
};
debounced.flush = () => {
if (timer) {
clearTimeout(timer);
timer = null;
return invoke();
}
return result;
};
debounced.pending = () => timer !== null;
return debounced as DebouncedFn<T>;
}
注意 invoke 里 leading 调用时会把 lastArgs 清空,但之后 trailing 的 setTimeout 仍会触发,此时 lastArgs 为空,invoke 返回 result,不会重复调用。不过有个细节:leading 调用后,如果用户在 delay 内又触发了一次,lastArgs 会被重新赋值,timer 重置,最终 trailing 会执行一次,这是正确的行为(标准 lodash 行为)。
不过还有个细节:leading 执行后,如果后续没有新调用,trailing 的 timer 到点后 invoke 发现 lastArgs 为 null,什么也不做——正确。
再给个 Promise 版本或者说明"等最后一次结果"的需求可以用 Promise 包装。
我用中文回答,配简单说明和用例。代码块用 js/ts。
还要提一下常见坑:
- this 绑定
- 参数透传
- 返回值
- 需要 cancel 清理(组件卸载)
保持简洁但实用。
基础版
function debounce(fn, delay = 300) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
够用,但少了几个实际开发中常要的能力:this 之外的入参透传已经做了,还缺 立即执行、取消、返回值。
进阶版(推荐)
type Debounced<T extends (...args: any[]) => any> = {
(...args: Parameters<T>): ReturnType<T> | undefined;
cancel(): void;
flush(): ReturnType<T> | undefined;
pending(): boolean;
};
function debounce<T extends (...args: any[]) => any>(
fn: T,
delay = 300,
{ leading = false, trailing = true } = {}
): Debounced<T> {
let timer: ReturnType<typeof setTimeout> | null = null;
let lastArgs: Parameters<T> | null = null;
let lastThis: any = null;
let result: ReturnType<T> | undefined;
function invoke() {
if (!lastArgs) return result;
result = fn.apply(lastThis, lastArgs);
lastArgs = lastThis = null;
return result;
}
function debounced(this: any, ...args: Parameters<T>) {
lastArgs = args;
lastThis = this;
const callNow = leading && !timer;
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
if (trailing) invoke();
}, delay);
if (callNow) result = invoke();
return result;
}
debounced.cancel = () => {
if (timer) clearTimeout(timer);
timer = null;
lastArgs = lastThis = null;
};
debounced.flush = () => {
if (timer) {
clearTimeout(timer);
timer = null;
return invoke();
}
return result;
};
debounced.pending = () => timer !== null;
return debounced as Debounced<T>;
}
用法
// 搜索框
const onSearch = debounce((kw) => fetch(`/api/search?q=${kw}`), 300);
input.addEventListener('input', (e) => onSearch(e.target.value));
// 按钮防连点:首次立即执行,防重复提交
const submit = debounce(doSubmit, 1000, { leading: true, trailing: false });
// 组件卸载前清理,避免定时器泄漏
onUnmounted(() => onSearch.cancel());
// 需要"立刻把这批输入提交掉"时
onSearch.flush();
几个容易踩的点
this和参数要透传:不用apply(this, args)的话,绑定在对象上会丢上下文。cancel必须做:Vue/React 组件卸载后定时器还在,回调里访问已销毁的响应式数据会报错。- 防抖 ≠ 节流:防抖是"停手后才执行",连续输入时一次都不触发;需要"匀速执行"(滚动、拖拽、resize)得用
throttle。 - 异步结果的坑:上面版本返回的是"最后一次同步调用"的值。如果
fn是 async,debounced()拿不到最终 Promise,需要额外做一个按 key 缓存 Promise 的版本,告诉我要不要写。 flush与leading同时用:leading执行后lastArgs已清空,此时flush()返回缓存结果而不会重复调用,这个是符合预期的。


用户询问这张图片是什么。让我仔细观察图片内容。
这是一张展示壮观瀑布的风景照片。图片中可以看到:
- 一条巨大的瀑布从悬崖上倾泻而下
- 瀑布周围是绿色的植被覆盖的山坡/悬崖
- 前景有一些绿色植物和野花
- 天空呈现紫灰色调,可能是阴天或黄昏时分
- 瀑布的水流呈现出丝滑的效果,这通常是长曝光摄影的结果
这个瀑布看起来非常像冰岛的塞里雅兰瀑布(Seljalandsfoss),这是冰岛最著名的瀑布之一,位于冰岛南部。它的特点是:
- 从悬崖边缘垂直落下
- 周围有茂密的绿色植被
- 游客可以走到瀑布后面
- 高度约60米
不过我也应该提到,如果没有更多确认信息,我可以描述图片内容,但具体地点是基于视觉特征的推测。
这是一张壮观的瀑布风景照片,从画面特征来看,很可能是冰岛的塞里雅兰瀑布(Seljalandsfoss)。
图片内容描述:
| 元素 | 详情 |
|---|---|
| 主体 | 一条宽阔的瀑布从悬崖顶端垂直倾泻而下 |
| 拍摄手法 | 使用了长曝光技术,使水流呈现丝滑、如纱般的质感 |
| 周围环境 | 两侧是覆盖着浓密绿色苔藓和植被的悬崖峭壁 |
| 前景 | 有茂盛的绿草和一些野花/植物点缀 |
| 天空 | 紫灰色调的云层,营造出神秘而宁静的氛围 |
| 水雾 | 瀑布底部升起大量水雾 |
关于塞里雅兰瀑布:
📍 位置:冰岛南部,位于环岛公路(Route 1)附近
📏 高度:约 60 米
✨ 特色:这是冰岛最具标志性的瀑布之一,游客可以沿着小径走到瀑布背后观赏,体验非常独特
📸 摄影圣地:尤其是日落时分和阳光穿透水雾形成彩虹时,景色绝美
这张照片的构图非常出色——前景的植物增加了层次感,长曝光让瀑布如丝绸般柔美,整体色调偏冷,充满了北欧自然的静谧与力量感。
