使用1个解析和1个拒绝链接异步Promises
Chaining async Promises with 1 resolve and 1 reject?
我有一个函数,它必须在几个步骤中执行异步操作。每走一步都可能失败。它可能在步骤1之前失败,因此您可能会立即或1.5秒后知道结果。当失败时,它必须运行回调。当成功时Idem。(我故意在时使用,因为不仅仅是:时间很重要。)
我认为Promises是完美的,因为async和它们只解决一次,但它仍然有一个问题:它什么时候失败?我可以清楚地看到它什么时候成功(在最后一步之后),但它什么时候失败?内部/任何步骤之前。
这就是我现在所拥有的,但这太荒谬了:
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()
随机通过或失败一步。)
Fiddle here: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
ing类似,并且不会执行链接的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
了,并且可以在.catch()
中移动console.log('rejecting...');
语句。
虽然我想这只是你的例子,所有步骤看起来都一样,但你也可以很容易地从列表中动态创建承诺链:
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());
}
由于您的时间安排是单调的,并且您的"工作"是预定义的,因此我会重构代码,以使用带有上下文的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
}
该函数可以用resoly/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
}
首先,让我们非常巧妙地重新排列开场白,以反映更典型、更恰当地使用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);
});
- 多个链接的Javascript动画
- 如何使用jQuery禁用多个链接
- 当页面包含指向一个图像的多个链接时,fancyBox会复制缩略图辅助图像
- 为什么在这种情况下要向JQuery添加两个链接
- 可折叠容器/柱.添加多个链接
- 是否有脚本/代码可以一次将链接属性添加到多个链接
- 将多个链接表映射到嵌套的JQuery手风琴
- jQuery show()和hide()查找页面上的多个链接
- Javascript在3个链接上添加EventListener,只有1个可用
- 单击时打开 2 个链接,而不会将一个链接作为“弹出窗口”阻止
- 在量角器中选择多个链接
- 设置 cookie 以检查是否已单击 2 个链接中的 1 个并通过 JS
- 传单使用多个链接缩放位置
- 使用 JavaScript 向一组链接中的第一个链接添加一个属性
- 我想使用 javascript 将第一个链接保留为默认选择
- 同时提取两个链接
- 可以使用一个函数调用 2 个链接的 Ajax
- 如何在单击同一链接时停止多个链接
- 仅在用户确认后打开多个链接
- 节点JS,从两个链接同步发出HTTPS请求