提问人:Revious 提问时间:1/15/2014 最后编辑:CommunityRevious 更新时间:1/19/2016 访问量:15876
为什么回调比承诺更“紧密耦合”?
Why are callbacks more "tightly coupled" than promises?
问:
你能解释一下下面这句话吗(摘自 Stack Overflow 问题的答案 Javascript 中的 Deferred、Promise 和 Future 有什么区别?
与使用以前的 jQuery 回调相比,使用 jQuery promise 有什么好处?
而不是直接将回调传递给函数,而是 可以导致紧密耦合的接口,使用 Promise 可以允许 对同步或异步代码的单独关注点。
答:
耦合与承诺更松散,因为操作不必“知道”它如何继续,它只需要知道何时准备就绪。
当你使用回调时,异步操作实际上有一个对它的延续的引用,这不是它的业务。
借助 Promise,您甚至可以在决定如何解析表达式之前,轻松地通过异步操作创建表达式。
因此,承诺有助于将链接事件与执行实际工作的问题分开。
评论
promise 是一个对象,它表示异步操作的结果,因此您可以传递它,这为您提供了更大的灵活性。
如果使用回调,则在调用异步操作时,必须指定如何处理它,因此需要耦合。使用 promise,您可以指定以后如何处理它。
下面是一个示例,假设您想通过 ajax 加载一些数据,同时您想显示一个加载页面。
使用回调:
void loadData = function(){
showLoadingScreen();
$.ajax("http://someurl.com", {
complete: function(data){
hideLoadingScreen();
//do something with the data
}
});
};
处理返回数据的回调必须调用 hideLoadingScreen。
使用 promises,您可以重写上面的代码片段,使其更具可读性,并且您不必将 hideLoadingScreen 放在完整的回调中。
有承诺
var getData = function(){
showLoadingScreen();
return $.ajax("http://someurl.com").promise().always(hideLoadingScreen);
};
var loadData = function(){
var gettingData = getData();
gettingData.done(doSomethingWithTheData);
}
var doSomethingWithTheData = function(data){
//do something with data
};
更新:我写了一篇博文,提供了额外的例子,并清楚地描述了什么是承诺,以及如何将其使用与使用回调进行比较。
评论
hideLoadingScreen
doSomethingWithTheData
hideLoadingScreen
doSomethingWithTheData
他们不是,这只是一种合理化,那些完全错过了承诺点的人用它来证明编写比使用回调编写的代码多得多的代码是合理的。鉴于这样做显然没有好处,你至少可以总是告诉自己代码耦合较少或其他什么。
了解什么是承诺,以及我为什么要将它们用于实际的具体利益。
评论
cb(err,result)
我不认为承诺或多或少与回调耦合,只是差不多。
然而,承诺还有其他好处:
如果公开一个回调,你必须记录它是被调用一次(如在jQuery.ajax中)还是多次(如在Array.map中)。承诺总是被调用一次。
没有办法调用回调抛出和异常,因此您必须为错误情况提供另一个回调。
只需注册一个回调,多个 promise 回调,您可以在活动结束后注册它们,无论如何您都会被调用。
在类型化声明 (Typescript) 中,Promise 使签名更易于阅读。
将来,您可以利用 async / yield 语法。
因为它们是标准的,所以您可以制作可重用的组件,如下所示:
disableScreen<T>(promiseGenerator: () => Promise<T>) : Promise<T> { //create transparent div return promiseGenerator.then(val=> { //remove transparent div return val; }, error=>{ //remove transparent div throw error; }); } disableScreen(()=>$.ajax(....));
更多信息:http://www.html5rocks.com/en/tutorials/es6/promises/
编辑:
- 另一个好处是编写 N 个异步调用序列,而没有 N 级缩进。
另外,虽然我仍然不认为这是重点,但现在我认为由于以下原因,它们耦合得更松散一些:
它们是标准的(或者至少是尝试的):使用字符串的 C# 或 Java 代码比 C++ 中的类似代码更糟糕,因为那里的字符串实现不同,使其更可重用。有了标准承诺,调用方和实现之间的耦合程度较低,因为它们不必就具有自定义参数 orders、names 等的(一对)自定义回调达成一致......事实上,承诺有许多不同的口味,这无助于思考。
它们促进了更基于表达式的编程,更易于组合、缓存等:
var cache: { [key: string] : Promise<any> }; function getData(key: string): Promise<any> { return cache[key] || (cache[key] = getFromServer(key)); }
你可以说,基于表达式的编程比基于命令式/回调的编程更松散地耦合,或者至少它们追求相同的目标:可组合性。
评论
yield
承诺重申了对某事的延迟响应的概念。 它们使异步计算成为一等公民,因为您可以传递它。它们允许您根据需要定义结构 - 一元结构 - 您可以在此基础上构建高阶组合器,从而大大简化代码。
例如,你可以有一个函数,它接受一个 promise 数组并返回一个数组的 promise(通常称为 )。这很难做到,甚至不可能通过回调。这样的组合器不仅使代码更易于编写,而且使代码更易于阅读。sequence
现在反过来考虑一下来回答你的问题。回调是一种临时解决方案,其中承诺允许更清晰的结构和可重用性。
评论
...normal Javascript code
比如?JavaScript promise 只是 ES6 原生的一部分。有关更多详细信息,请参阅此处: html5rocks.com/en/tutorials/es6/promises 关于一般意义上的回调与承诺,随着抽象和解耦的越多,您就越能更好地测试您的代码并分离关注点(请注意,您可能会过度抽象,但那是另一回事)。Promise 允许您进行单个焦点操作,而不必知道之前或之后会发生什么。func(arg1, arg2, callback)
func(arg1, arg2, [optional]arg3, callback
)