Javascript 什么是显式承诺构造反模式?如何避免它?
我正在编写代码,它可以执行如下操作:Javascript 什么是显式承诺构造反模式?如何避免它?,javascript,promise,q,bluebird,es6-promise,Javascript,Promise,Q,Bluebird,Es6 Promise,我正在编写代码,它可以执行如下操作: function getStuffDone(param) { | function getStuffDone(param) { var d = Q.defer(); /* or $q.defer */ | return new Promise(function(resolve, reject) { // or = new $.Deferred() etc. | // using a promi
function getStuffDone(param) { | function getStuffDone(param) {
var d = Q.defer(); /* or $q.defer */ | return new Promise(function(resolve, reject) {
// or = new $.Deferred() etc. | // using a promise constructor
myPromiseFn(param+1) | myPromiseFn(param+1)
.then(function(val) { /* or .done */ | .then(function(val) {
d.resolve(val); | resolve(val);
}).catch(function(err) { /* .fail */ | }).catch(function(err) {
d.reject(err); | reject(err);
}); | });
return d.promise; /* or promise() */ | });
} | }
有人告诉我,这分别被称为“延迟反模式”或“承诺”
构造函数反模式”,这段代码有什么不好的地方,为什么被称为?这是由一个普通的反模式的人创造的,他们是新做出承诺的,当我第一次使用承诺时,我自己做的。上述代码的问题在于,is未能利用承诺链这一事实
承诺可以链接到。然后
,您可以直接返回承诺。getStuffDone
中的代码可以重写为:
function getStuffDone(param){
return myPromiseFn(param+1); // much nicer, right?
}
承诺就是让异步代码更具可读性,并像同步代码一样运行,而不隐藏这一事实。承诺表示对一次性操作值的抽象,它们抽象了编程语言中语句或表达式的概念
只有当您无法自动执行延迟对象时,或者当您正在编写更容易用这种方式表示的聚合函数时,才应该使用延迟对象
引用Esailija的话:
这是最常见的反模式。当您不真正理解承诺并将其视为美化的事件发射器或回调实用程序时,很容易陷入这种情况。让我们回顾一下:承诺是让异步代码保留同步代码丢失的大部分属性,例如平面缩进和一个异常通道
怎么了?
但这种模式是有效的
你真幸运。不幸的是,它可能没有,因为您可能忘记了一些边缘案例。在我看到的一半以上的事件中,作者忘记了处理错误处理程序:
return new Promise(function(resolve) {
getOtherPromise().then(function(result) {
resolve(result.property.example);
});
})
如果另一个承诺被拒绝,这将在不被注意的情况下发生,而不是传播到新的承诺(它将被处理的地方)——而新的承诺将永远处于等待状态,这可能导致泄漏
在回调代码导致错误的情况下也会发生同样的情况,例如,result
没有属性,并且引发异常。这将无法解决,新的承诺也无法解决
相反,使用.then()
会自动处理这两种情况,并在发生错误时拒绝新承诺:
return getOtherPromise().then(function(result) {
return result.property.example;
})
延迟的反模式不仅麻烦,而且容易出错。使用.then()
进行链接更安全
但我已经处理好了一切
真的吗?好。然而,这将是非常详细和丰富的,特别是如果您使用支持其他功能(如取消或消息传递)的promise库。或者将来会这样,或者你想用一个更好的库来交换你的库?你不会想为此重写你的代码
这些库的方法(然后
)不仅在本机上支持所有功能,还可能进行了某些优化。使用它们可能会使您的代码更快,或者至少允许库的未来版本对其进行优化
我如何避免它?
因此,每当您发现自己手动创建了承诺
或延迟承诺
,并且已经存在承诺时,首先检查库API。延迟反模式通常由那些将承诺[仅]视为观察者模式的人应用,但是:它们应该是可组合的。每一个像样的库都有许多易于使用的函数,用于以各种可想象的方式组合承诺,处理所有您不想处理的低级内容
如果您发现需要以现有帮助函数不支持的新方式编写某些承诺,那么编写自己的函数时不可避免地要延迟,这应该是您最后的选择。考虑切换到更具特色的库,和/或对当前库进行bug处理。它的维护者应该能够从现有功能中派生出组合,为您实现一个新的助手功能和/或帮助识别需要处理的边缘情况。7年后,这个问题有一个更简单的答案:
如何避免显式构造函数反模式?
使用异步函数
s,然后等待
每一个承诺
而不是手动构建嵌套的承诺链,例如:
function promised() {
return new Promise(function(resolve) {
getOtherPromise().then(function(result) {
getAnotherPromise(result).then(function(result2) {
resolve(result2);
});
});
});
}
只需将函数async
,并使用wait
关键字停止函数的执行,直到承诺得到解决:
async function promised() {
const result = await getOtherPromise();
const result2 = await getAnotherPromise(result);
return result2;
}
这有多种好处:
- 调用
async
函数总是返回一个承诺,该承诺将使用返回的值进行解析,并在异步函数中抛出错误get时拒绝
- 如果一个
wait
ed承诺被拒绝,那么错误get会在异步函数中抛出,因此您可以try{…}catch(error){…}
它就像同步错误一样
- 您可以在循环和if分支中
等待
,使大多数承诺链逻辑变得微不足道
- 虽然异步函数的行为大多类似于承诺链,但它们更易于阅读(也更易于推理)
我如何等待回调?
如果回调只回调一次,而您正在调用的API尚未提供承诺(大多数都提供!),这是使用承诺构造函数的唯一原因:
// Create a wrapper around the "old" function taking a callback, passing the 'resolve' function as callback
const delay = time => new Promise((resolve, reject) =>
setTimeout(resolve, time)
);
await delay(1000);
如果wait
停止执行,调用异步函数是否直接返回结果?
否。如果调用异步函数,则始终会返回承诺。然后,您可以在异步函数中等待这一承诺。您不能在同步函数中等待结果(您必须调用。然后
并附加回调)
概念上