Promise 从未打算用作布尔值,但这实际上isGood()
是在做的事情。在这里,我们不仅仅是指用布尔值解决/拒绝一个承诺。我们的意思是,一个承诺的状态传达了它的状态:
有些人可能认为这是滥用承诺,但尝试以这种方式利用承诺很有趣。
可以说,关于 Promise 作为布尔值的主要问题是:
- “真”的承诺表示将走成功路径,“假”的承诺表示将走失败路径
- Promise 库自然不会允许所有必要的布尔代数 - 例如。非、与、或、异或
在更好地探索和记录该主题之前,克服/利用这些功能需要想象力。
让我们尝试解决这个问题(使用 jQuery - 我更了解它)。
首先让我们写一个更明确的版本isGood()
:
/*
* A function that determines whether a number is an integer or not
* and returns a resolved/rejected promise accordingly.
* In both cases, the promise is resolved/rejected with the original number.
*/
function isGood(number) {
return $.Deferred(function(dfrd) {
if(parseInt(number, 10) == number) {
setTimeout(function() { dfrd.resolve(number); }, 100);//"true"
} else {
setTimeout(function() { dfrd.reject(number); }, 100);//"false"
}
}).promise();
}
我们将需要一个“NOT”方法——交换“已解决”和“已拒绝”的方法。jQuery Promise 没有本地逆变器,所以这里有一个函数来完成这项工作。
/*
* A function that creates and returns a new promise
* whose resolved/rejected state is the inverse of the original promise,
* and which conveys the original promise's value.
*/
function invertPromise(p) {
return $.Deferred(function(dfrd) {
p.then(dfrd.reject, dfrd.resolve);
});
}
现在,问题的一个版本findGoodNumber()
,但在这里利用重写isGood()
和invertPromise()
实用程序。
/*
* A function that accepts an array of numbers, scans them,
* and returns a resolved promise for the first "good" number,
* or a rejected promise if no "good" numbers are present.
*/
function findGoodNumber(numbers) {
if(numbers.length === 0) {
return $.Deferred.reject().promise();
} else {
return invertPromise(numbers.reduce(function(p, num) {
return p.then(function() {
return invertPromise(isGood(num));
});
}, $.when()));
}
}
最后,相同的调用例程(数据略有不同):
var arr = [3.1, 9.6, 17.0, 26.9, 89];
findGoodNumber(arr).then(function(goodNumber) {
console.log('Good number found: ' + goodNumber);
}, function() {
console.log('No good numbers found');
});
演示
将代码转换回 Angular/$q 应该非常简单。
解释
of的else
子句findGoodNumber()
可能不太明显。它的核心是numbers.reduce(...)
,它构建了一个.then()
链 - 有效地对numbers
阵列进行异步扫描。这是一种熟悉的异步模式。
在没有两次反转的情况下,将扫描数组,直到找到第一个错误数字,并且结果拒绝将采用失败路径(跳过扫描的其余部分并继续进行失败处理程序)。
但是,我们想找到第一个走“失败”路径的好数字——因此需要:
- 内部反转:将报告的“真”转换为“假” - 强制跳过扫描的其余部分
- 外部反转:恢复原始的布尔意义——“真”以“真”结束,“假”以“假”结束。
您可能需要弄乱演示以更好地了解正在发生的事情。
结论
是的,无需递归即可解决问题。
这个解决方案既不是最简单的也不是最有效的,但是它有希望展示 Promise 状态表示布尔值和实现异步布尔代数的潜力。
替代解决方案
findGoodNumber()
可以通过执行“或扫描”来编写而无需反转,如下所示:
function findGoodNumber(numbers) {
if(numbers.length === 0) {
return $.Deferred.reject().promise();
} else {
return numbers.reduce(function(p, num) {
return p.then(null, function() {
return isGood(num);
});
}, $.Deferred().reject());
}
}
这是 Bergi 解决方案的 jQuery 等价物。
演示