Promise中resolve回调时setTimeout未生效问题求助
问题与修复方案
我用JavaScript写了一个基于Promise的函数,实现网页逐步滚动到底部,短暂延迟后平滑滚回页面顶部,原本想通过setTimeout延迟调用回调函数,但回调似乎被立即resolve了。我的目标是等页面滚动回顶部后再延迟触发回调。
以下是我的原代码:
// main scroll function async function promiseScroll(callback = null) { return new Promise((resolve, reject) => { function pageScroll() { const scrollIncrement = document.body.offsetHeight / 5 window.scrollBy({ top: scrollIncrement, left: 0, behavior: "smooth" }) let scrollDelay = setTimeout(pageScroll, 1000) if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) { clearTimeout(scrollDelay) setTimeout(resolve(promisePageUp(callback)), 2000) } } pageScroll() }) } // scroll the page to top async function promisePageUp(callback = null) { return new Promise((resolve, reject) => { window.scrollTo({ top: 0, left: 0, behavior: "smooth" }) // delayedCallback might not be needed // the invocation of callback is not delayed for some reason setTimeout(resolve(delayedCallback(callback)), 2000) }) } // delay resolving of callback but this does not work async function delayedCallback(callback = null) { return new Promise((resolve, reject) => { // expect the callback to be called after 2 seconds // issue might be because of `callback()`, but we cannot do `callback` either setTimeout(resolve(callback()), 2000) }) } // sample callback for testing async function callback() { return "callback is called!" } // Run promised based scroll simulation // This is a async IIFE (async () => { const value = await promiseScroll(callback) console.log(value) })()
核心问题分析
所有setTimeout(resolve(xxx), 2000)的写法都是错误的——resolve(xxx)会立即执行,而不是作为回调传给setTimeout。setTimeout的第一个参数必须是一个函数,否则会直接执行表达式,导致Promise被提前resolve。另外,scrollTo的smooth行为是异步的,不能直接用固定超时判断滚动完成,需要监听scrollend事件确认滚动结束。
修复后的代码
// main scroll function async function promiseScroll(callback = null) { return new Promise((resolve, reject) => { function pageScroll() { const scrollIncrement = document.body.offsetHeight / 5; window.scrollBy({ top: scrollIncrement, left: 0, behavior: "smooth" }); const scrollDelay = setTimeout(pageScroll, 1000); // 检测是否滚动到底部,减1避免精度误差 if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight - 1) { clearTimeout(scrollDelay); // 2秒延迟后执行滚回顶部逻辑 setTimeout(() => { resolve(promisePageUp(callback)); }, 2000); } } pageScroll(); }); } // scroll the page to top async function promisePageUp(callback = null) { return new Promise((resolve, reject) => { window.scrollTo({ top: 0, left: 0, behavior: "smooth" }); // 监听scrollend事件,确认滚动到顶部完成 const onScrollEnd = () => { window.removeEventListener("scrollend", onScrollEnd); // 滚动完成后延迟2秒触发回调 setTimeout(async () => { if (callback) { const result = await callback(); resolve(result); } else { resolve(); } }, 2000); }; window.addEventListener("scrollend", onScrollEnd); }); } // sample callback for testing async function callback() { return "callback is called!"; } // Run promised based scroll simulation // This is a async IIFE (async () => { const value = await promiseScroll(callback); console.log(value); })()
关键修复点
- 修正setTimeout调用方式:将
setTimeout(resolve(xxx), 2000)改为setTimeout(() => resolve(xxx), 2000),确保resolve在超时后才执行。 - 监听scrollend事件:替代固定超时,准确捕获平滑滚动的完成时机,避免滚动还没结束就触发后续逻辑。
- 简化回调处理:移除冗余的
delayedCallback函数,直接在滚动完成后的超时里处理回调调用和Promise resolve,逻辑更清晰。 - 滚动到底部的精度优化:判断条件减1,避免因浏览器高度计算的微小误差导致无法触发底部检测。
内容的提问来源于stack exchange,提问作者Dale Ryan
相关产品推荐
相关产品推荐

