[英]Chaining async Promises with 1 resolve and 1 reject?
我有一个必须在几步中完成异步操作的函数。 在每个步骤中它都可能失败。 它可能在步骤1之前失败,因此您可能立即或在1.5秒后知道结果。 如果失败,则必须运行回调。 同上, 当成功。 (我故意的时候 ,使用,因为它不只是如 :时机很重要)
我认为Promises是完美的,因为异步并且它们只能解决一次,但是仍然有一个问题:何时失败? 我可以清楚地看到它何时成功(在最后一步之后),但是何时失败? 在任何步骤之前/之前。
这就是我现在所拥有的,但这太荒谬了:
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/
我猜解决方案是链许诺,解决或拒绝每一步。 也许。 那是东西吗? 我将如何实施?
可以执行未知数量的步骤吗?
您可以从字面上重写您对诺言的解决方案:
function sleep(ms) {
return new Promise(function(resolve) {
setTimeout(resolve, ms);
});
}
function clickSpeed() {
if ( test('one') ) {
return sleep(500).then(function() {
if ( test('two') ) {
return sleep(500).then(function() {
if ( test('three') ) {
return sleep(500).then(function() {
console.log('resolving...');
});
}
console.log('rejecting...');
return Promise.reject();
});
}
console.log('rejecting...');
return Promise.reject();
});
}
console.log('rejecting...');
return Promise.reject();
}
但是,这仍然很丑陋。 我宁愿将if/else
颠倒为第一步:
function clickSpeed() {
if (!test('one')) {
console.log('rejecting...');
return Promise.reject();
}
return sleep(500).then(function() {
if (!test('two')) {
console.log('rejecting...');
return Promise.reject();
}
return sleep(500).then(function() {
if (!test('three')) {
console.log('rejecting...');
return Promise.reject();
}
return sleep(500).then(function() {
console.log('resolving...');
});
});
});
}
但是,我们也可以取消嵌套这些回调。 当您使用if
进行分支时,通常是不可能的,但是在这种情况下,唯一的替代结果是拒绝,这就像throw
,并且不会执行链接的then
回调一样。
function clickSpeed() {
return Promise.resolve() // only so that the callbacks look alike, and to use throw
.then(function() {
if (!test('one')) {
console.log('rejecting...');
throw;
}
return sleep(500);
}).then(function() {
if (!test('two')) {
console.log('rejecting...');
throw;
}
return sleep(500)
}).then(function() {
if (!test('three')) {
console.log('rejecting...');
throw;
}
return sleep(500);
}).then(function() {
console.log('resolving...');
});
}
现在,你可以把这些test
之遥例外本身,所以你不需要if
任何更多的,你可以移动console.log('rejecting...');
.catch()
语句。
虽然我猜这只是所有步骤看起来都一样的示例,但是您也可以轻松地从列表中动态创建承诺链:
function clickSpeed() {
return ['one', 'two', 'three'].reduce(function(p, cur) {
return p.then(function() {
if (!test(cur))
throw new Error('rejecting...');
else
return sleep(500);
});
}, Promise.resolve());
}
首先,让我们非常巧妙地重新排列开场白,以反映对诺言的更典型,更恰当的使用。
代替 :
只需几步即可完成异步功能的功能
比方说:
一个必须在几个异步步骤中执行某些操作的函数
因此,我们可能首先选择编写一个函数,该函数执行一个Promise链并返回其最终的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);
});
尽管doAnAsyncSequence()
或doSomethingAsync()
都没有console.log()
语句:
这就是理论。
回答特定问题(据我了解)...
对于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);
});
由于您的时间安排是单调的,并且您的“工作”是预定义的,因此我将重构代码以利用setInterval()
和上下文来跟踪所需的“工作”或“步骤”。 实际上,尽管您仍然可以使用Promise,但您甚至不必使用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
}
该函数可以使用“解析/拒绝”处理程序“直接”调用,也可以用作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
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.