我正在使用返回JSON的API,在此页面上我有一个进度条,指示了根据用户的请求设置内容的各个步骤。每个后续的AJAX请求的成功回调都会启动下一个请求,因为它们需要按顺序完成。一步发出服务器端后台作业,端点返回事务ID。
在此流程之外,有一个函数可以检查另一个端点以查看此事务是否完成。如果它“待定”,我需要在一小段延迟后重新发出请求。
我使用了递归函数:
function checkTransaction(trxid) {
window.profileTrx[trxid] = 0;
trxurl = 'https://example.com/transaction/'+trxid;
$.getJSON(trxurl,function(result) {
if(result.status === 'pending') {
setTimeout(function () {
checkTransaction(trxid);
},3000);
} else {
window.profileTrx[trxid] = result;
}
});
}
我使用窗口的原因是我可以通过它在来自它的回调中的ID来访问事务 - 如果有的话,这是一个很好的用例。但它变得凌乱,我缺乏经验开始妨碍我。循环遍历window.profileTrx[trxid]
似乎是双重工作,并且没有按预期运行,循环太快并且崩溃了页面。同样,.then()
中的下一步承诺是我的想法,但我无法弄清楚如何。
我如何使用promises实现这一点,以便启动递归“事务检查”的回调函数只有在API返回对检查的非挂起响应后才会继续执行其余的执行?
我可以让我的头围绕递归并返回一个承诺,但不能同时回复。任何和所有的帮助都受到了广泛的赞赏。
答案 0 :(得分:3)
当我首先考虑承诺时,我的头脑总是更清晰:
// wrap timeout in a promise
function wait(ms) {
var deferred = $.Deferred();
setTimeout(function() {
deferred.resolve();
}, ms);
return deferred.promise();
}
// promise to get a trxid
function getTRX(trxid) {
var trxurl = 'https://example.com/transaction/'+trxid;
return $.getJSON(trxurl);
}
现在原来的功能似乎很简单......
function checkTransaction(trxid) {
window.profileTrx[trxid] = trxid;
return getTRX(trxid).then(function(result) {
if (result.status === 'pending') {
return wait(3000).then(function() {
return checkTransaction(trioxid);
});
} else {
window.profileTrx[trxid] = result;
return result;
}
});
}
来电者将如下所示:
return checkTransaction('id0').then(function(result) {
return checkTransaction('id1');
}).then(function(result) {
return checkTransaction('id2');
}) // etc
请记住,如果checkTransaction在很长一段时间内保持待定状态,那么您将建立很长的承诺链。确保get以3000ms的非常小的倍数返回。
答案 1 :(得分:1)
由于您在问题中使用jQuery,我将首先提出一个使用基于$.Deferred()
对象的jQuery承诺实现的解决方案。正如@Bergi所指出的,这是considered an antipattern。
// for this demo, we will fake the fact that the result comes back positive
// after the third attempt.
var attempts = 0;
function checkTransaction(trxid) {
var deferred = $.Deferred();
var trxurl = 'http://echo.jsontest.com/status/pending?' + trxid;
function poll() {
console.log('polling...');
// Just for the demo, we mock a different response after 2 attempts.
if (attempts == 2) {
trxurl = 'http://echo.jsontest.com/status/done?' + trxid;
}
$.getJSON(trxurl, function(result) {
if (result.status === 'pending') {
console.log('result:', result);
setTimeout(poll, 3000);
} else {
deferred.resolve('final value!');
}
});
// just for this demo
attempts++;
}
poll();
return deferred.promise();
}
checkTransaction(1).then(function(result) {
console.log('done:', result)
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
这应该有效(运行代码段来查看),但正如链接答案中所提到的,这种“延迟”模式存在问题,例如未报告错误情况。
问题是jQuery承诺(直到最近的版本 - 我没有检查过)have massive issues that prevent better patterns from being used。
另一种方法是使用专用的promise库,它在then()
函数上实现正确的链接,这样你就可以以更强大的方式编写函数并避免使用“延迟的”反模式:
对于真正的承诺组合,它避免完全使用“延迟”对象,我们可以使用更符合要求的承诺库,例如Bluebird。在下面的代码片段中,我使用的是Bluebird,它为我们提供了一个可以正常运行的Promise
对象。
function checkTransaction(trxid) {
var trxurl = 'http://echo.jsontest.com/status/pending?' + trxid;
var attempts = 0;
function poll() {
if (attempts == 2) {
trxurl = 'http://echo.jsontest.com/status/done?' + trxid;
}
attempts++;
console.log('polling...');
// wrap jQuery's .getJSON in a Bluebird promise so that we
// can chain & compose .then() calls.
return Promise.resolve($.getJSON(trxurl)
.then(function(result) {
console.log('result:', result);
if (result.status === 'pending') {
// Bluebird has a built-in promise-ready setTimeout
// equivalent: delay()
return Promise.delay(3000).then(function() {
return poll();
});
} else {
return 'final value!'
}
}));
}
return poll();
}
checkTransaction(1).then(function(result) {
console.log('done:', result);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.4.1/bluebird.min.js"></script>
答案 2 :(得分:0)
您可以从函数返回promise,并且当所有返回的promise都被解析时,父函数的.then
将解析。
查看详细信息。
https://gist.github.com/Bamieh/67c9ca982b20cc33c9766d20739504c8