thu*_*uld 12 javascript jquery asynchronous promise bluebird
我为Dynamics CRM REST/ODATA webservice(CrmRestKit)开发了一个小型lib .lib依赖于jQuery并使用promise模式,后者是jQuery的类似promise的模式.
现在我想将此lib移植到bluebird并删除jQuery依赖项.但是我遇到了一个问题,因为bluebird不支持promise-objects的同步解析.
一些上下文信息:
CrmRestKit的API除了可选参数之外,该参数定义是否应该以同步或异步模式执行Web服务调用:
CrmRestKit.Create( 'Account', { Name: "foobar" }, false ).then( function ( data ) {
....
} );
Run Code Online (Sandbox Code Playgroud)
当您传递"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');
Run Code Online (Sandbox Code Playgroud)
输出如下:
print -> second
print -> first
Run Code Online (Sandbox Code Playgroud)
我希望"第二个"出现在"第一个"之后,因为承诺已经用一个值来解决.所以我假设当应用于已经解析的promise-object时会立即调用then-event-handler .
当我使用jQuery执行相同的操作(然后使用已经解决的承诺)时,我将获得预期的结果:
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');
Run Code Online (Sandbox Code Playgroud)
这将生成以下输出:
print -> third
print -> fourth
Run Code Online (Sandbox Code Playgroud)
有没有办法让蓝鸟以同样的方式工作?
更新: 提供的代码只是为了说明问题.lib的思想是:无论执行模式(sync,async)如何,调用者总是会处理promise-object.
关于"...询问用户......似乎没有任何意义":当您提供两种方法"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 ){
// ...
}
Run Code Online (Sandbox Code Playgroud)
我同意当"GetCurrentUserRoles"方法从同步更改为异步时,此代码将中断.
但我明白这不是一个好设计,因为消费者现在应该处理异步方法.
Joe*_*han 18
简短版本:我明白你想要这样做,但答案是否定的.
我认为要问的根本问题是,如果承诺已经完成,完成的承诺是否应立即进行回调.我可以想到很多可能会发生这种情况的原因 - 例如,异步保存过程只会在进行更改时保存数据.它可能能够以同步方式检测来自客户端的变化,而不必经过外部资源,但是如果检测到变化,则只需要进行异步操作.
在其他具有异步调用的环境中,模式似乎是开发人员有责任了解他们的工作可能会立即完成(例如,.NET框架的异步模式实现可以适应这种情况).这不是框架的设计问题,而是它的实现方式.
JavaScript的开发人员(以及上面的许多评论者)似乎对此有不同的观点,坚持认为如果某些东西可能是异步的,它必须始终是异步的.这是否"正确"是不重要的 - 根据我在https://promisesaplus.com/上找到的规范,第2.2.4项规定,基本上没有回调可以被调用,直到你没有我所指的作为"脚本代码"或"用户代码"; 也就是说,规范明确指出,即使承诺已经完成,您也无法立即调用回调.我已经检查过其他几个地方,他们要么就此话题一无所知,要么同意原始来源.我不知道是否https://promisesaplus.com/ 在这方面可以被认为是最终的信息来源,但我认为没有其他来源不同意,而且似乎是最完整的.
这个限制有些武断,我坦率地更喜欢.NET的观点.我将把它留给其他人来决定他们是否认为"坏代码"做一些可能会或可能不会以看起来异步的方式同步的东西.
您的实际问题是Bluebird是否可以配置为执行非JavaScript行为.在性能方面,这样做可能会带来一些小好处,而在JavaScript中,如果你足够努力就可以做任何事情,但随着Promise对象在各个平台上变得越来越普遍,你会看到转向使用它作为本机组件而不是自定义编写polyfill或库.因此,无论今天的答案是什么,在Bluebird中重新修改承诺可能会在将来导致问题,并且您的代码可能不应该依赖于或立即解决承诺.
您可能认为这是一个问题,因为没有办法
getSomeText('first').then(print);
print('second');
Run Code Online (Sandbox Code Playgroud)
并且在分辨率同步getSomeText
"first"
之前打印"second"
.
但我认为你有一个逻辑问题.
如果您的getSomeText
函数可能是同步的或异步的,则根据上下文,它不应该影响执行的顺序.你使用promises来确保它始终是一样的.具有可变执行顺序可能会成为应用程序中的错误.
使用
getSomeText('first') // may be synchronous using cast or asynchronous with ajax
.then(print)
.then(function(){ print('second') });
Run Code Online (Sandbox Code Playgroud)
在这两种情况下(同步cast
或异步解析),您将拥有正确的执行顺序.
请注意,使某个函数有时是同步的,有时不是同步的(不管是缓存处理还是池化).你只需假设它是异步的,一切都会好起来的.
但是如果你不想离开JavaScript的领域(即如果你不使用一些本机代码),如果他希望操作是异步的,那么要求API的用户精确地使用布尔参数似乎没有任何意义. ).
承诺的目的是使异步代码更容易,即更接近使用同步代码时的感觉.
您正在使用同步代码.不要让它变得更复杂.
function print( text ){
console.log( 'print -> %s', text );
return text;
}
function getSomeTextSimpleCast( opt_text ){
var text = opt_text || 'Some fancy text-value';
return text;
}
print(getSomeTextSimpleCast('first'));
print('second');
Run Code Online (Sandbox Code Playgroud)
这应该是它的结束.
如果你想保持相同的异步接口,即使你的代码是同步的,那么你必须一直这样做.
getSomeTextSimpleCast('first')
.then(print)
.then(function() { print('second'); });
Run Code Online (Sandbox Code Playgroud)
then
从正常的执行流程中获取代码,因为它应该是异步的.蓝鸟在那里做得很好.它的作用的简单解释:
function then(fn) {
setTimeout(fn, 0);
}
Run Code Online (Sandbox Code Playgroud)
请注意,bluebird并不是真的这样做,它只是给你一个简单的例子.
试试吧!
then(function() {
console.log('first');
});
console.log('second');
Run Code Online (Sandbox Code Playgroud)
这将输出以下内容:
second
first
Run Code Online (Sandbox Code Playgroud)