【发布时间】:2014-02-03 20:30:51
【问题描述】:
我为Dynamics CRM REST/ODATA webservice (CrmRestKit) 开发了一个小库。该库依赖于 jQuery,并利用了 promise-pattern,也就是 jQuery 的 promise-like-pattern。
现在我想将此库移植到 bluebird 并删除 jQuery 依赖项。但我面临一个问题,因为蓝鸟不支持承诺对象的同步解析。
一些上下文信息:
CrmRestKit 的 API 除外一个可选参数,该参数定义 Web 服务调用应以同步还是异步模式执行:
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
我希望“第二个”出现在“第一个”之后,因为承诺已经用一个值解决了。所以我会假设一个 then-event-handler 当应用于已经解析的 promise-object 时会立即被调用。
当我用 jQuery 做同样的事情(在已经解决的承诺上使用 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
有没有办法让蓝鸟以同样的方式工作?
更新: 提供的代码只是为了说明问题。 lib 的想法是:无论执行模式(同步、异步)如何,调用者都将始终处理一个承诺对象。
关于“......询问用户......似乎没有任何意义”:当您提供两种方法“CreateAsync”和“CreateSync”时,用户也可以决定如何执行操作.
无论如何,对于当前实现,默认行为(最后一个参数是可选的)是异步执行。所以 99% 的代码需要一个 promise-object,可选参数仅用于 1% 的情况,即您只需要同步执行。此外,我为自己开发了 lib,并且在 99,9999% 的情况下使用异步模式,但我认为可以选择按照自己的喜好选择同步模式是件好事。
但我认为我明白了同步方法应该简单地返回值。对于下一个版本(3.0),我将实现“CreateSync”和“CreateAsync”。
感谢您的意见。
Update-2 我对可选参数的意图是确保一致的行为并防止逻辑错误。假设您是我使用 lib 的方法“GetCurrentUserRoles”的消费者。所以这个方法总是会返回一个promise,这意味着你必须使用“then”方法来执行依赖于结果的代码。所以当有人写这样的代码时,我同意这是完全错误的:
var currentUserRoels = null;
GetCurrentUserRoles().then(function(roles){
currentUserRoels = roles;
});
if( currentUserRoels.indexOf('foobar') === -1 ){
// ...
}
我同意当“GetCurrentUserRoles”方法从同步更改为异步时,此代码将中断。
但我知道这不是一个好的设计,因为消费者现在应该处理异步方法。
【问题讨论】:
-
如果调用是同步的,为什么你会返回一个承诺?正常返回值即可。
-
我想指出,以上评论来自Bluebird的作者。
-
@dystroy 不,它是调用者选择同步的重载函数(2 个不同的函数)。 API 也可以是
CrmRestKit.CreateAsync和CrmRestKit.CreateSync,而不是CrmRestKit.Create({async: ?})。这与有时可能同步知道其返回值的函数完全不同(例如,用于数据库调用的一级缓存) -
@thuld 绝对不是,同步函数必须返回直接值。如果您必须使用带有同步函数的 Promise,这是最糟糕的 API。 jQuery 所做的事情是非常错误的,因为当一个函数真正同步和异步时,它会导致不可预知的执行顺序——即调用者无法决定的时候。此外,从同步函数返回值和从异步函数返回值根本不是“不同的 API”。例如,查看 node fs API 或 XMLHttpRequest - 在使用同步调用时,它们都不会强制您使用回调或承诺。
-
OP,我认为您有足够的信息来接受其中一个答案或自行选择答案,不是吗?
标签: javascript jquery asynchronous promise bluebird