我对 debounce axios 请求的实现使承诺永远处于挂起状态,有没有更好的方法?
Posted
技术标签:
【中文标题】我对 debounce axios 请求的实现使承诺永远处于挂起状态,有没有更好的方法?【英文标题】:My implementation of debounce axios request left the promise in pending state forever, is there a better way? 【发布时间】:2019-09-19 01:02:29 【问题描述】:我需要一个简单的 debounce 函数,它总是立即为真。 在不求助于 lodash 的情况下,在 Can someone explain the "debounce" function in javascript 的帮助下,我将其实现如下,
function debounce(func, wait)
var timeout;
return function()
if (!timeout) func.apply(this, arguments);
clearTimeout(timeout);
timeout = setTimeout(()=>timeout = null, wait);
;
;
在我需要去抖动 axios 请求之前,它会按预期工作。假设我有一个去抖动的 axios 方法,我希望调用方法像往常一样,这意味着我的去抖动 axios 方法应该返回我相信的承诺。
//the calling method should not change
debounced_axios().then(res => ...).catch(err => ...)
原始去抖动实现的本质是在等待时间范围内只运行 func 一次,但是我如何在等待时间范围内只返回 一个 承诺?
然后我想出了以下解决方案
all_timers =
function debounce_axios(input, wait)
return new Promise((resolve, reject) =>
let timer = all_timers.[input] //check if it is a repeated request, pseudo code
if (!timer)
axios(input).then(res=>
resolve(res)
).catch(err =>
reject(err)
)
clearTimeout(timer);
timer = setTimeout(()=>timer = null, wait);
all_timers[input] = timer
;
;
所以我的debounce_axios的本质是让promise对于重复请求保持在pending状态。那么调用方法debounced_axios().then(res => ...).catch(err => ...)
就不需要改变了。
这里的答案Are JavaScript forever-pending promises bad?说“应该没有副作用。”
但我仍然不能 100% 确定让承诺永远处于未决状态。
另一个问题是Promise Anti patterns 建议不要创建不必要的承诺。但就我而言,创建一个新的承诺似乎是必要的。
简而言之,有没有一种简单的方法来消除 axios 请求(或任何请求返回承诺)?
【问题讨论】:
“.then(res => ...).catch(err => ...)
”到底是做什么的?为什么不简单地把它放在你用来创建debounced_axios
的函数中?
各种偶数处理程序来操作 DOM,因此我无法在 debounced_axios 中输入它们
好吧,也许你不应该再叫它debounced_axious
,但是为什么不完全消除整个事情呢?
是的,我同意去抖动 ui 可能是一个更好的主意。但事实上它是一个 SPA 应用程序,代码库很大,需要修改的地方太多,而我们确实将 axios 方法包装在一个 util 类中,因此很容易修改它。顺便说一句,我从你那里得到了这个想法。谢谢
如果您不喜欢永远未决的承诺,是否可以立即拒绝过早提出的请求?
【参考方案1】:
但我仍然不能 100% 确定让承诺永远处于未决状态。
我同意这不是一个好主意。更好的方法是将整个 Promise 链移动到 debounced 函数中。
另一种选择是在去抖动调用未触发新请求时返回缓存值。这将解决您始终需要返回承诺的问题:
function debounce(func, wait)
var timeout, value;
return function()
if (!timeout) value = func.apply(this, arguments);
clearTimeout(timeout);
timeout = setTimeout(() =>
timeout = value = null;
, wait);
return value;
;
当然,这意味着在某些情况下,当您的请求完成时,会调用多个 then
处理程序。这取决于您的应用程序这是一个问题还是只是多余的工作。
另一个问题是 Promise Anti 模式建议不要创建不必要的承诺。但就我而言,创建一个新的承诺似乎是必要的。
只有一个承诺是必要的:当你创建一个从未解决的承诺时。你可以写成
function debounce(func, wait)
var timeout;
const never = new Promise(resolve => /* do nothing*/);
return function()
const result = timeout ? never : func.apply(this, arguments);
clearTimeout(timeout);
timeout = setTimeout(() =>
timeout = null;
, wait);
return result;
;
或者至少避免使用.then(resolve).catch(reject)
部分。写得更好
function debounce(func, wait)
var timeout;
return function()
return new Promise(resolve =>
if (!timeout) resolve(func.apply(this, arguments));
// ^^^^^^^
clearTimeout(timeout);
timeout = setTimeout(() =>
timeout = null;
, wait);
);
;
如果您考虑在尚未发生超时的情况下拒绝承诺(以便调用代码可以处理拒绝),您也不需要new Promise
:
function debounce(func, wait)
var timeout;
return function()
const result = timeout
? Promise.reject(new Error("called during debounce period"))
: Promise.resolve(func.apply(this, arguments));
clearTimeout(timeout);
timeout = setTimeout(() =>
timeout = null;
, wait);
return result;
;
【讨论】:
您好,我正在验证您的每一个建议。对于返回缓存值,我不确定它是否会起作用。因为我假设缓存的承诺将在调用方法的承诺链中被多次调用,对吧? 我以前从未缓存过承诺,所以我想我是在问一个新手问题。那么缓存 Promise 并重用它是安全的吗?使用缓存的 Promise 运行 Promise 链是否安全? 是的,caching promises 完全正常并且运行良好。 Promise 不是“调用”的,它是一个结果值 - 请求只会发出一次,但响应将传递给在.then()
注册的尽可能多的处理程序。
您好,我仍在验证您的答案:$。对于“或者至少避免使用 .then(resolve).catch(reject) 部分。”我认为您为去抖动调用返回了一个未决的永远承诺,就像创建从未解决的承诺一样。我们回到第一方,即我最初的问题是这样做好吗?不是这样吗?
@Qiulang 是的,我认为至少有 3 种更好的方法,但是如果您仍然想做,那么至少要正确地做(如第二个或第三个 sn-p 所示)。
【参考方案2】:
本质上,您需要分享去抖动功能的结果。在你的情况下,这是一个承诺:
const debouncedGetData = debounce(getData, 500)
let promiseCount = 0
let resultCount = 0
test()
function test()
console.log('start')
callDebouncedThreeTimes()
setTimeout(callDebouncedThreeTimes, 200)
setTimeout(callDebouncedThreeTimes, 900)
function callDebouncedThreeTimes ()
for (let i=0; i<3; i++)
debouncedGetData().then(r =>
console.log('Result count:', ++resultCount)
console.log('r', r)
)
function debounce(func, wait)
let waiting;
let sharedResult;
return function()
// first call will create the promise|value here
if (!waiting)
setTimeout(clearWait, wait)
waiting = true
sharedResult = func.apply(this, arguments);
// else new calls within waitTime will be discarded but shared the result from first call
function clearWait()
waiting = null
sharedResult = null
return sharedResult
;
function getData ()
console.log('Promise count:', ++promiseCount)
return new Promise((resolve, reject) =>
setTimeout(() =>
resolve(666)
, 1000)
)
【讨论】:
我认为是不正确的 b/c (a) 正常的去抖动方法应该返回去抖动的方法 (b) 在 axios 情况下,当去抖动的情况没有返回任何返回时,调用方法会得到 UnhandledPromiseRejectionWarning: TypeError: (...).then 不是函数。 我相信这里的 debounce 函数可以用于任何类型的函数,因为它不依赖于 promises 谢谢,我会核实并回复您。以上是关于我对 debounce axios 请求的实现使承诺永远处于挂起状态,有没有更好的方法?的主要内容,如果未能解决你的问题,请参考以下文章