jQuery Deferred 的递归 Promise 函数有什么问题
What is wrong with this recursive Promise function with jQuery Deferred
我正在为 ReST API 进程编写脚本。现在我需要一个函数,它在几秒钟内不断重试向 API 请求尽可能多的请求。
所以我写了一些 Promise 抽象,并做了如下内容:
$(function () {
var $el = $('#test');
function api (message) {
return $.ajax('/echo/json/', {
method: 'POST',
data: { json: JSON.stringify({ message: message }), delay: 2000 },
timeout: 1000
});
}
// Process to keep retrying an API request as many as possible in 3 seconds
retried(_.bind(api, undefined, 'Hello, world.'), 3000)
.then(function (dat) {
$el.text(dat.message);
}, function (err) {
if (err instanceof Error) $el.css('color', 'red');
$el.text(err.message);
});
});
上面我做的部分功能如下:
// Promise wrapper
var promisify = function (func) {
var funcPartial = function () {
var funcArgs = _.toArray(arguments);
var dfr = new $.Deferred(), promiseArgs = [ dfr.resolve, dfr.reject ];
var timeoutId = setTimeout(function () {
clearTimeout(timeoutId);
func.apply(undefined, _.union(promiseArgs, funcArgs));
}, 1);
return dfr.promise();
};
return funcPartial;
};
// Promise abstraction for recursive call
retried = promisify(function (resolve, reject, done, duration, start) {
if (!_.isNumber(start)) start = +(new Date());
return done()
.then(resolve, function (err) {
var stop = +(new Date());
if (duration <= stop - start) {
reject(err);
} else {
return retried(done, duration, start);
}
});
});
进程在正确的条件下完成,但 retried
函数不会 return Promise 链。
我真的很累。有人能指出我错误的地方来纠正上面的实现吗?
这是完整的演示脚本。
谢谢。
已解决
感谢下面的@BenjaminGruenbaum,我刚刚注意到我根本不需要 promisify
来使 retried
起作用。这是一个完全可耻的问题,但再次感谢所有回答这个问题的人。
这是修改后的retried
函数,根本不需要promisify
...
var retried = function (done, duration, start) {
if (!_.isNumber(start)) start = +(new Date());
return done()
.then(function (dat) {
return dat;
}, function (err) {
var stop = +(new Date());
if (duration > stop - start) return retried(done, duration, start);
return err;
});
};
我更新了演示,现在可以正常使用了 XD
DEMO(修订)
你重试的逻辑很复杂。您是 "promisifying" 一个具有解决和拒绝并执行 explicit construction 的函数。您将如何在同步代码中实现重试?
do {
var failed = false;
try {
var val = fn();
} catch(e){
failed = true;
}
} while(failed);
但我们不能真正做到这一点,因为我们不能使用循环,但是我们可以使用递归:
function retry(fn){
try {
return fn(); // call the function
} catch(e){
return retry(fn); // retry it again
}
}
现在,在 just 中添加承诺意味着失败不是通过 try/catch 而是通过承诺的 resolution/lack of:
function retry(fn) {
return fn().then(null, function(e){
return retry(fn); // on failure handler, retry again.
});
}
此实现的好处是不依赖于所使用的承诺库(它将使用相同的类型 fn
returns)。
作为旁注,请不要忽略错误。甚至网络错误 - 至少在某处记录它们:)
我正在为 ReST API 进程编写脚本。现在我需要一个函数,它在几秒钟内不断重试向 API 请求尽可能多的请求。
所以我写了一些 Promise 抽象,并做了如下内容:
$(function () {
var $el = $('#test');
function api (message) {
return $.ajax('/echo/json/', {
method: 'POST',
data: { json: JSON.stringify({ message: message }), delay: 2000 },
timeout: 1000
});
}
// Process to keep retrying an API request as many as possible in 3 seconds
retried(_.bind(api, undefined, 'Hello, world.'), 3000)
.then(function (dat) {
$el.text(dat.message);
}, function (err) {
if (err instanceof Error) $el.css('color', 'red');
$el.text(err.message);
});
});
上面我做的部分功能如下:
// Promise wrapper
var promisify = function (func) {
var funcPartial = function () {
var funcArgs = _.toArray(arguments);
var dfr = new $.Deferred(), promiseArgs = [ dfr.resolve, dfr.reject ];
var timeoutId = setTimeout(function () {
clearTimeout(timeoutId);
func.apply(undefined, _.union(promiseArgs, funcArgs));
}, 1);
return dfr.promise();
};
return funcPartial;
};
// Promise abstraction for recursive call
retried = promisify(function (resolve, reject, done, duration, start) {
if (!_.isNumber(start)) start = +(new Date());
return done()
.then(resolve, function (err) {
var stop = +(new Date());
if (duration <= stop - start) {
reject(err);
} else {
return retried(done, duration, start);
}
});
});
进程在正确的条件下完成,但 retried
函数不会 return Promise 链。
我真的很累。有人能指出我错误的地方来纠正上面的实现吗?
这是完整的演示脚本。
谢谢。
已解决
感谢下面的@BenjaminGruenbaum,我刚刚注意到我根本不需要 promisify
来使 retried
起作用。这是一个完全可耻的问题,但再次感谢所有回答这个问题的人。
这是修改后的retried
函数,根本不需要promisify
...
var retried = function (done, duration, start) {
if (!_.isNumber(start)) start = +(new Date());
return done()
.then(function (dat) {
return dat;
}, function (err) {
var stop = +(new Date());
if (duration > stop - start) return retried(done, duration, start);
return err;
});
};
我更新了演示,现在可以正常使用了 XD
DEMO(修订)
你重试的逻辑很复杂。您是 "promisifying" 一个具有解决和拒绝并执行 explicit construction 的函数。您将如何在同步代码中实现重试?
do {
var failed = false;
try {
var val = fn();
} catch(e){
failed = true;
}
} while(failed);
但我们不能真正做到这一点,因为我们不能使用循环,但是我们可以使用递归:
function retry(fn){
try {
return fn(); // call the function
} catch(e){
return retry(fn); // retry it again
}
}
现在,在 just 中添加承诺意味着失败不是通过 try/catch 而是通过承诺的 resolution/lack of:
function retry(fn) {
return fn().then(null, function(e){
return retry(fn); // on failure handler, retry again.
});
}
此实现的好处是不依赖于所使用的承诺库(它将使用相同的类型 fn
returns)。
作为旁注,请不要忽略错误。甚至网络错误 - 至少在某处记录它们:)