我有一个功能,必须在几个步骤中执行异步操作。在每一步都可能失败。它可能在步骤1之前失败,因此您可能立即知道结果,或者在1.5秒之后。 失败时,必须运行回调。同意 成功时。 (我正在使用 时,因为它不仅仅是如果:时机很重要。)
我认为Promise是完美的,因为异步并且它们只解决了一次,但它仍然有问题:什么时候失败?我可以明确地看到它何时成功(在最后一步之后),但什么时候失败?在任何步骤内/之前。
这就是我现在所拥有的,但那太荒谬了:
function clickSpeed() {
return new Promise(function(resolve, reject) {
if ( test('one') ) {
return setTimeout(function() {
if ( test('two') ) {
return setTimeout(function() {
if ( test('three') ) {
return setTimeout(function() {
console.log('resolving...');
resolve();
}, 500);
}
console.log('rejecting...');
reject();
}, 500);
}
console.log('rejecting...');
reject();
}, 500);
}
console.log('rejecting...');
reject();
});
}
(test()
随机传递或失败一步。)
在这里小提琴:http://jsfiddle.net/rudiedirkx/zhdrjjx1/
我猜测解决方案是链接承诺,解决或拒绝每一步......?也许。那是一件事吗?我该如何实现呢?
这可以用于未知数量的步骤吗?
答案 0 :(得分:1)
首先,让我们巧妙地重新安排开场陈述,以反映更为典型和更恰当的使用承诺。
而不是:
一个必须执行异步的功能,只需几步
让我们说:
一个必须在几个异步步骤
中执行某些操作的函数
因此,我们可能首先选择编写一个执行promise链并返回其结果的函数的函数:
function doAnAsyncSequence() {
return Promise.resolve()
.then(function() {
doSomethingAsync('one');
})
.then(function() {
doSomethingAsync('two');
})
.then(function() {
doSomethingAsync('three');
});
}
并且,为了演示,我们可以编写doSomethingAsync()
以便它返回一个具有50:50被解决概率的承诺:被拒绝(这比延迟更有用):
function doSomethingAsync(x) {
return new Promise(function(resolve, reject) {
if(Math.random() > 0.5 ) {
resolve(x);
} else {
reject(x); // importantly, this statement reports the input argument `x` as the reason for failure, which can be read and acted on where doSomethingAsync() is called.
}
});
}
然后,问题的核心部分:
什么时候失败?
可能会改写:
什么时候失败了?
这是一个更现实的问题,因为我们通常会调用我们几乎没有影响的异步进程(它们可能在世界其他地方的某个服务器上运行),我们希望它会成功但可能会随机失败。如果是这样,我们的代码(和/或我们的最终用户)想知道哪一个失败了,为什么。
在doAnAsyncSequence()
的情况下,我们可以这样做:
doAnAsyncSequence().then(function(result) {
console.log(result); // if this line executes, it will always log "three", the result of the *last* step in the async sequence.
}, function(reason) {
console.log('error: ' + reason);
});
虽然console.log()
或doAnAsyncSequence()
中没有doSomethingAsync()
声明:
这就是理论。
回答具体问题(据我所知)......
代表doSomethingAsync()
,写道:
function test_(value, delay) {
return new Promise(function(resolve, reject) {
//call your own test() function immediately
var result = test(value);
// resolve/reject the returned promise after a delay
setTimeout(function() {
result ? resolve() : reject(value);
}, delay);
});
}
代表doAnAsyncSequence()
,写道:
function clickSpeed() {
var delayAfterTest = 500;
return Promise.resolve()
.then(function() {
test_('one', delayAfterTest);
})
.then(function() {
test_('two', delayAfterTest);
})
.then(function() {
test_('three', delayAfterTest);
});
}
请致电如下:
clickSpeed().then(function() {
console.log('all tests passed');
}, function(reason) {
console.log('test sequence failed at: ' + reason);
});
答案 1 :(得分:1)
由于你的时间是单调的,而且你的工作时间很长。是预定义的,我重构代码以利用setInterval()
和上下文来跟踪所需的工作"或者"步骤"。事实上,即使你仍然可以 使用来使用Promise,尽管你仍然可以,如果你想进一步链接处理程序,那么这是一个好主意:
function clickSpeed(resolve, reject) {
var interval = setInterval(function(work) {
try {
var current = work.shift();
if(!test(current)) { // Do current step's "work"
clearInterval(interval); // reject on failure and clear interval
console.log('rejecting...', current);
reject();
}
else if(!work.length) { // If this was the last step
clearInterval(interval); // resolve (success!) and clear interval
console.log('resolving...');
resolve();
}
}
catch(ex) { // reject on exceptions as well
reject(ex);
clearInterval(interval);
}
}, 500, ['one', 'two', 'three']); // "work" array
}
该功能可以直接调用""使用resolve / reject处理程序,或用作Promise构造函数的参数。
请参见修改后的JSFiddle中的完整示例。
要解决Bergi的太多样板注释,可以更简洁地编写代码,而无需记录:
function clickSpeed(resolve, reject) {
function done(success, val) {
clearInterval(interval);
success ? resolve(val) : reject(val);
}
var interval = setInterval(function(work) {
try {
if(test(work.shift()) || done(false)) {
work.length || done(true);
}
}
catch(ex) { // reject on exceptions as well
done(false, ex);
}
}, 500, ['one', 'two', 'three']); // "work" array
}