我为 Dynamics CRM REST/ODATA webservice 开发了一个小库(CrmRestKit)。该库依赖于 jQuery 并使用 promise-pattern,分别是 jQuery 的 promise-like-pattern。
现在我想将这个库移植到 bluebird并删除 jQuery 依赖项。但是我遇到了一个问题,因为bluebird不支持promise-objects的同步解析。
一些上下文信息:
CrmRestKit 的 API 除了一个可选参数外,该参数定义了网络服务调用应该以同步还是异步模式执行:
CrmRestKit.Create( 'Account', { Name: "foobar" }, false ).then( function ( data ) {
....
} );
当您传递“true”或省略最后一个参数时,该方法将同步创建记录。模式。
有时需要在同步模式下执行操作,例如您可以为 Dynamics CRM 编写 JavaScript 代码,该代码涉及表单的保存事件,并且在此事件处理程序中您需要执行同步操作用于验证(例如,验证是否存在一定数量的子记录,如果存在正确数量的记录,则取消保存操作并显示错误消息)。
我现在的问题是:bluebird 不支持同步模式下的分辨率。例如,当我执行以下操作时,将以异步方式调用“then”处理程序:
function print( text ){
console.log( 'print -> %s', text );
return text;
}
///
/// 'Promise.cast' cast the given value to a trusted promise.
///
function getSomeTextSimpleCast( opt_text ){
var text = opt_text || 'Some fancy text-value';
return Promise.cast( text );
}
getSomeTextSimpleCast('first').then(print);
print('second');
输出如下:
print -> second
print -> first
我希望“第二个”出现在“第一个”之后,因为 promise 已经用一个值解决了。因此,我假设当应用于已解析的 promise 对象时 立即调用 then-event-handler。
当我用 jQuery 做同样的事情(在已经解决的 promise 上使用 then)时,我将得到预期的结果:
function jQueryResolved( opt_text ){
var text = opt_text || 'jQuery-Test Value',
dfd = new $.Deferred();
dfd.resolve(text);
// return an already resolved promise
return dfd.promise();
}
jQueryResolved('third').then(print);
print('fourth');
这将生成以下输出:
print -> third
print -> fourth
有没有办法让 Bluebird 以同样的方式工作?
更新: 提供的代码只是为了说明问题。 lib 的想法是:无论执行模式如何(同步、异步),调用者将始终处理 promise 对象。
关于“...询问用户...似乎没有任何意义”:当您提供“CreateAsync”和“CreateSync”两种方法时,也由用户决定如何执行操作.
无论如何,当前实现的默认行为(最后一个参数是可选的)是异步执行。所以 99% 的代码需要一个 promise-object,可选参数只用于 1% 的情况,你只需要一个同步执行。此外,我为自己开发了 lib,并且在 99,9999% 的情况下我使用异步模式,但我认为可以选择随心所欲地走同步之路是件好事。
但我认为我理解了同步方法应该简单地返回值的要点。对于下一个版本 (3.0),我将实现“CreateSync”和“CreateAsync”。
感谢您的输入。
更新 2 我对可选参数的意图是确保一致的行为并防止逻辑错误。假设您是我使用 lib 的方法“GetCurrentUserRoles”的消费者。所以该方法将始终返回一个 promise ,这意味着您必须使用“then”方法来执行依赖于结果的代码。所以当有人写这样的代码时,我同意这是完全错误的:
var currentUserRoels = null;
GetCurrentUserRoles().then(function(roles){
currentUserRoels = roles;
});
if( currentUserRoels.indexOf('foobar') === -1 ){
// ...
}
我同意,当“GetCurrentUserRoles”方法从同步更改为异步时,此代码将中断。
但我知道这不是一个好的设计,因为消费者现在应该处理异步方法。
最佳答案
简短版本:我明白你为什么要这样做,但答案是否定的。
我认为被问到的根本问题是,如果 promise 已经完成,完成的 promise 是否应该立即运行回调。我可以想到很多可能发生这种情况的原因——例如,一个异步保存过程只在发生更改时才保存数据。它可能能够以同步方式从客户端检测更改,而无需通过外部资源,但如果检测到更改,那么并且只有在那时才需要异步操作。
在具有异步调用的其他环境中,模式似乎是开发人员负责了解他们的工作可能会立即完成(例如,.NET Framework 的异步模式实现适应了这一点)。这不是框架的设计问题,而是它的实现方式。
JavaScript 的开发人员(以及上面的许多评论者)对此似乎有不同的观点,坚持认为如果某些东西可能是异步的,那么它必须始终是异步的。这是否“正确”并不重要——根据我在 https://promisesaplus.com/ 找到的规范, item 2.2.4 声明基本上没有回调可以被调用,直到你离开我将称之为“脚本代码”或“用户代码”的地方;也就是说,规范明确指出,即使 promise 已完成,您也不能立即调用回调。我检查了其他几个地方,他们要么对这个话题什么也没说,要么同意原始来源。我不知道https://promisesaplus.com/可以被认为是这方面的权威信息来源,但我看到没有其他来源不同意它,而且它似乎是最完整的。
这个限制有点武断,坦率地说,我更喜欢 .NET 的观点。我会留给其他人来决定他们是否认为以看起来异步的方式执行可能同步或不同步的事情是“糟糕的代码”。
您的实际问题是 Bluebird 是否可以配置为执行非 JavaScript 行为。在性能方面,这样做可能会有一点好处,如果你足够努力,在 JavaScript 中一切皆有可能,但随着 Promise 对象在平台上变得越来越普遍,你会看到将其用作 native 组件而不是自定义编写的转变polyfill 或库。因此,无论今天的答案是什么,在 Bluebird 中修改 promise 很可能会在未来给您带来问题,并且您的代码可能不应该被编写为依赖于 promise 或提供 promise 的立即解决方案。
关于javascript - 同步 promise 解析(bluebird vs. jQuery),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21136283/