Javascript 将jQuery$.ajax包装在q.js中的facade后面而不进行重构

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 );

有可能这应该在代码审查中,但我们开始了

我有一个相当大的应用程序,有很多ajax调用。我开始用Q来处理一些异步的东西,我想我应该用Q来包装ajax调用,以确保所有异步方法都有相同的签名

我使用的是全局facade方法,因此我的ajax调用如下所示:

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