Javascript 将jQuery$.ajax包装在q.js中的facade后面而不进行重构
有可能这应该在代码审查中,但我们开始了 我有一个相当大的应用程序,有很多ajax调用。我开始用Q来处理一些异步的东西,我想我应该用Q来包装ajax调用,以确保所有异步方法都有相同的签名 我使用的是全局facade方法,因此我的ajax调用如下所示:Javascript 将jQuery$.ajax包装在q.js中的facade后面而不进行重构,javascript,jquery,ajax,promise,q,Javascript,Jquery,Ajax,Promise,Q,有可能这应该在代码审查中,但我们开始了 我有一个相当大的应用程序,有很多ajax调用。我开始用Q来处理一些异步的东西,我想我应该用Q来包装ajax调用,以确保所有异步方法都有相同的签名 我使用的是全局facade方法,因此我的ajax调用如下所示: App.ajax( config ).then( doWhatever ); App.ajax看起来像这样: ajax: function( config ){ var ajaxReturn = $.ajax( config );
App.ajax( config ).then( doWhatever );
App.ajax看起来像这样:
ajax: function( config ){
var ajaxReturn = $.ajax( config );
ajaxReturn.error(function( xhr ){
// some custom error handling
});
return ajaxReturn;
}
ajax: function( config ){
var dfd = Q.defer();
var ajaxReturn = $.ajax( config );
ajaxReturn.done(function( results, status, xhr ){
delete xhr.then;
dfd.resolve( results );
});
ajaxReturn.error(function( xhr ){
// some custom error handling
dfd.reject( "some message generated by error handling" );
});
return dfd.promise;
}
ajax: function( config ){
var dfd = Q.defer();
var ajaxReturn = $.ajax( config );
ajaxReturn.done(function( results, status, xhr ){
delete xhr.then;
dfd.resolve( [ results, status, xhr ] );
});
ajaxReturn.error(function( xhr ){
// some custom error handling
dfd.reject( "some message generated by error handling" );
});
dfd.promise.then = function( callback ){
dfd.promise.then = Q().then;
return dfd.promise.spread( callback );
};
return dfd.promise;
}
我对App.ajax进行了如下修改:
ajax: function( config ){
var ajaxReturn = $.ajax( config );
ajaxReturn.error(function( xhr ){
// some custom error handling
});
return ajaxReturn;
}
ajax: function( config ){
var dfd = Q.defer();
var ajaxReturn = $.ajax( config );
ajaxReturn.done(function( results, status, xhr ){
delete xhr.then;
dfd.resolve( results );
});
ajaxReturn.error(function( xhr ){
// some custom error handling
dfd.reject( "some message generated by error handling" );
});
return dfd.promise;
}
ajax: function( config ){
var dfd = Q.defer();
var ajaxReturn = $.ajax( config );
ajaxReturn.done(function( results, status, xhr ){
delete xhr.then;
dfd.resolve( [ results, status, xhr ] );
});
ajaxReturn.error(function( xhr ){
// some custom error handling
dfd.reject( "some message generated by error handling" );
});
dfd.promise.then = function( callback ){
dfd.promise.then = Q().then;
return dfd.promise.spread( callback );
};
return dfd.promise;
}
这是可行的,每个ajax调用本身需要0个更改,但它导致关心$.ajax的“状态”和“xhr”部分的ajax调用停止工作
我读过,它基本上只是建议
现在,我不想重构所有关心xhr对象的调用,以获取对象或使用spread而不是then/done。因此,我在返回Q承诺之前添加了以下代码:
dfd.promise.then = function( callback ){
dfd.promise.then = Q().then;
return dfd.promise.spread( callback );
};
并将我的解析行更改为:
dfd.resolve( [ results, status, xhr ] );
现在App.ajax看起来是这样的:
ajax: function( config ){
var ajaxReturn = $.ajax( config );
ajaxReturn.error(function( xhr ){
// some custom error handling
});
return ajaxReturn;
}
ajax: function( config ){
var dfd = Q.defer();
var ajaxReturn = $.ajax( config );
ajaxReturn.done(function( results, status, xhr ){
delete xhr.then;
dfd.resolve( results );
});
ajaxReturn.error(function( xhr ){
// some custom error handling
dfd.reject( "some message generated by error handling" );
});
return dfd.promise;
}
ajax: function( config ){
var dfd = Q.defer();
var ajaxReturn = $.ajax( config );
ajaxReturn.done(function( results, status, xhr ){
delete xhr.then;
dfd.resolve( [ results, status, xhr ] );
});
ajaxReturn.error(function( xhr ){
// some custom error handling
dfd.reject( "some message generated by error handling" );
});
dfd.promise.then = function( callback ){
dfd.promise.then = Q().then;
return dfd.promise.spread( callback );
};
return dfd.promise;
}
因此,这是通过一个包装器函数超越了这个特定的延迟的then函数,这个包装器函数将重置为“real”,然后使用spread代替这个调用。这使得我的应用程序ajax调用能够保留其原始签名,但仍然是一个彻头彻尾的Q承诺
这似乎很好,这很好
现在,最后,对于问题,这是明智的吗?我理解在自定义“then”方法中不必要地创建额外的承诺对性能的影响很小。我才不管呢。我更想知道的是,是否有什么抓不到我的东西,这绝对是一个非常糟糕的主意
编辑:
上述模块存在一些问题。这是最新、最正确的代码块。承诺的then
不能只是重置为“真实的”then
,因为对初始承诺的后续调用无法获得正确的排列结果,因此我们必须将then方法重置为在返回新排列承诺之前调用排列的过度使用的方法
ajax: function( config ){
var dfd = Q.defer();
var ajaxReturn = $.ajax( config );
ajaxReturn.done(function( results, status, xhr ){
delete xhr.then;
dfd.resolve( [ results, status, xhr ] );
});
ajaxReturn.error(function( xhr ){
// some custom error handling
dfd.reject( "some message generated by error handling" );
});
var cachedThen = dfd.promise.then;
dfd.promise.then = function overrideThen( fulfilled, rejected ){
dfd.promise.then = cachedThen;
var spreadPromise = dfd.promise.spread( fulfilled, rejected );
dfd.promise.then = overrideThen;
return spreadPromise;
};
return dfd.promise;
}
你想得太多了。Q设计用于与jQuery承诺进行互操作 如果要将函数转换为返回Q承诺,只需使用
Q()
将其包装即可:
例如,您的ajax
方法可以是:
function ajax(config){
return Q($.ajax(config));
}
它更短,更不容易出错。你想得太多了。Q设计用于与jQuery承诺进行互操作 如果要将函数转换为返回Q承诺,只需使用
Q()
将其包装即可:
例如,您的ajax
方法可以是:
function ajax(config){
return Q($.ajax(config));
}
它更短,更不容易出错。似乎是为了一个好主意(将jQuery的承诺包装成Q承诺),你想出了两三个坏主意。你用dfd.promise做的那些事是完全疯狂的,它不起作用。注意:
var p = App.ajax({ url: "..." });
p.then(function (data) {
// ok, no problem
console.log(data);
});
// if p is not hopelessly broken at this point, the following statement
// should have exactly the same outcome as the previous one
p.then(function (data) {
// Logs "undefined"! You REPLACED p's .then() method with
// one from an empty promise in the last statement!
console.log(data);
});
即使你找到了解决上述特定问题的方法,在没有深刻理解其含义的情况下做这类事情也是不明智的。我没有详细阅读Q库的源代码,但是如果存在一些依赖于假设then
方法没有被替换为其他方法的内部依赖,我也不会感到惊讶
正如本杰明·格伦鲍姆所说,不要想得太多。不要试图把承诺变成他们不是为了避免更新你的代码。承诺/A+兼容承诺只将一个参数传递到它们的
.then()
处理程序中。如果你试图绕过这一点,你就完全破坏了你最初的好主意 似乎是为了一个好主意(将jQuery的承诺包装成Q承诺),你想出了两三个坏主意。你用dfd.promise做的那些事是完全疯狂的,它不起作用。注意:
var p = App.ajax({ url: "..." });
p.then(function (data) {
// ok, no problem
console.log(data);
});
// if p is not hopelessly broken at this point, the following statement
// should have exactly the same outcome as the previous one
p.then(function (data) {
// Logs "undefined"! You REPLACED p's .then() method with
// one from an empty promise in the last statement!
console.log(data);
});
即使你找到了解决上述特定问题的方法,在没有深刻理解其含义的情况下做这类事情也是不明智的。我没有详细阅读Q库的源代码,但是如果存在一些依赖于假设then
方法没有被替换为其他方法的内部依赖,我也不会感到惊讶
正如本杰明·格伦鲍姆所说,不要想得太多。不要试图把承诺变成他们不是为了避免更新你的代码。承诺/A+兼容承诺只将一个参数传递到它们的
.then()
处理程序中。如果你试图绕过这一点,你就完全破坏了你最初的好主意 我所能提供的最好的东西与问题中的一些东西并没有太大的不同,但是它更经济地机械化,并利用Q的能力强制执行jQuery承诺,正如Benjamin G所建议的那样
首先是效用函数:
function unspread() {
return Array.prototype.slice.call(arguments);
}
现在,在您的ajax()
函数中,您可以使用unpread()
实用程序捆绑jQuery的多个参数:
function ajax(options) {
return Q($.ajax(options).then(unspread, unspread));
}
不幸的是,由于Q的.spread()
方法的性质,成功和失败处理程序必须是不同的,它只在成功时传播,而不是在失败时传播
function foo(options) {
return ajax(options).spread(function(response, textStatus, xhr) {
console.dir(response);
console.log(textStatus);
console.dir(xhr);
return response;//or rebundle the args into an array/object
}, function(arr) {
console.dir(arr[0]);//xhr
console.log(arr[1]);//textStatus
console.dir(arr[2]);//ErrorThrown
throw arr;//or any of its elements
});
}
如果您真的希望在Q中使用命名错误参数,那么这里有一种非常混乱(且未经测试)的方法:
function foo(options) {
return ajax(options).spread(function(response, textStatus, xhr) {
console.dir(response);
console.log(textStatus);
console.dir(xhr);
return response;
}, function(err) {
if($.isArray(err)) {
//it's a $.ajax error array
return err; //put the error array on the success path so it can be spread
} else {
throw err;//maybe it's an unbundeled error
}
}).spread(function(xhr, textStatus, ErrorThrown) {
if(arguments.length == 3) {//test because the genuine success path will lead you here too.
console.dir(xhr);
console.log(textStatus);
console.dir(ErrorThrown);
}
});
}
但是,即使你能做到这一点,仅仅获得命名的args也是相当极端的
我相信可以编写一个“Q.superSpread()”方法来完成这项工作。我给了它10分钟的时间,认为它不是微不足道的,同样极端,而且可能在概念上是不合理的。我所能提供的最好的东西与问题中的一些东西并没有太大的不同,但是它更经济地机械化了,并且利用了Q的能力来强制实现jQuery承诺,正如Benjamin G所建议的那样 首先是效用函数:
function unspread() {
return Array.prototype.slice.call(arguments);
}
现在,在您的ajax()
函数中,您可以使用unpread()
实用程序来bun