【问题标题】:Generic AJAX function callback is not working as expected通用 AJAX 函数回调未按预期工作
【发布时间】:2015-02-18 13:31:47
【问题描述】:

我创建了通用 ajax 函数来使用 REST API。

我已经编写了下面的代码,一次调用它就可以正常工作。但如果我打了两个不同的电话,那么我的成功回调将被最新的回调覆盖。

var ajax = {
    parameters: {
        url: '',
        dataType: 'json',
        data: {},
        contentType: 'application/json',
        loading: false,
        crossDomain: true,
        async: true,
        timeout: 30000,
        otherData: {}
    },
    callback: {
        success: null,
        error: null
    },
    get: function () {
        this.send('GET');
    },
    post: function () {
        this.send('POST');
    },
    del: function () {
        this.send('DELETE');
    },
    send: function (type) {
        self = this;
        if (!navigator.userAgent.match(/iPad|iPhone|iPod|android|blackberry|IEMobile/i)) {
            this.loadingIcon.show();

            if (this.isUndefined(this.parameters.dataType)) this.parameters.dataType = 'json';
            if (this.isUndefined(this.parameters.contentType)) this.parameters.contentType = 'application/json';
            if (this.isUndefined(this.parameters.loading)) this.parameters.loading = false;
            if (this.isUndefined(this.parameters.crossDomain)) this.parameters.crossDomain = true;
            if (this.isUndefined(this.parameters.async)) this.parameters.async = true;
            if (this.isUndefined(this.parameters.timeout)) this.parameters.timeout = 30000;
            if (this.isUndefined(this.callback.error)) this.callback.error = this.defaultError;

            return $.ajax({
                type: type,
                url: this.parameters.url,
                dataType: this.parameters.dataType,
                data: JSON.stringify(this.parameters.data),
                contentType: this.parameters.contentType,
                crossDomain: this.parameters.crossDomain,
                async: this.parameters.async,
                timeout: this.parameters.timeout,
                success: function (data) {
                    var args = arguments[2];
                    if (!self.isUndefined(self.callback.success)) {
                        if (self.isUndefined(self.parameters.otherData))
                            self.callback.success.call(this, data, args.statusText, args);
                        else
                            self.callback.success.call(this, data, args.statusText, args, self.parameters.otherData);
                    }
                    self.loadingIcon.hide();
                },
                error: function (xhr, ajaxOptions, thrownError) {
                    xhrServerObj = xhr;
                    self.callback.error.call(xhr, ajaxOptions, thrownError);
                    self.loadingIcon.hide();
                }
            });
        }
    },
    isUndefined: function (param) {
        return (typeof param == 'undefined' || param == null);
    },
    defaultError: function (data, textStatus, jqXHR) {
    },
    loadingIcon: {
        show: function () {
            if (self.parameters.loading) {
                console.log('Show loading....');
            }
        },
        hide: function () {
            if (self.parameters.loading) {
                console.log('Hide loading....');
            }
        }
    }
}

我正在使用下面的 ajax 调用

getAllStores(); 
getAllCategory();

function getAllStores() {
    var req = Object.create(ajax);
    req.parameters = { url: API + '/api/mobileapp/stores' };
    req.callback.success = successGetAllStores;
    req.get();
}
function successGetAllStores(data) {
    $.each(data, function (idx, d) {
        $("#StoreId").append($("<option value='" + d.StoreId + "'>" + d.StoreName + "</option>"));
    });
}

function getAllCategory() {
    var req = Object.create(ajax);
    req.parameters = { url: API + '/api/mobileapp/categories' };
    req.callback.success = successGetAllCategory;
    req.get();
}
function successGetAllCategory(data) {
    $.each(data, function (idx, d) {
        $("#CategoryId").append($("<option value='" + d.CategoryId + "'>" + d.CategoryName + "</option>"));
    });
}

如果我一次只调用一个函数 getAllStores(); 那么在它成功回调时我再次调用 getAllCategory() 那么这是有效的。不覆盖现有的回调函数。

你能帮我如何使用通用 ajax 调用来调用多个调用来处理单个回调。

此外,我尝试过使用下面的方法,但它仍然被覆盖了最新的回调。

function MyAjax() {
    this.parameters = {
        url: '',
        dataType: 'json',
        data: {},
        contentType: 'application/json',
        loading: false,
        crossDomain: true,
        async: true,
        timeout: 30000,
        otherData: {}
    };
    this.callback = {
        success: null,
        error: null
    };
    this.get = function () {
        this.send('GET');
    };
    this.post = function () {
        this.send('POST');
    };
    this.del = function () {
        this.send('DELETE');
    };
    this.isUndefined = function (param) {
        return (typeof param == 'undefined' || param == null);
    };
    this.defaultError = function (data, textStatus, jqXHR) {
    };
    this.loadingIcon = {
        show: function () {
            if (self.parameters.loading) {
                console.log('Show loading....');
            }
        },
        hide: function () {
            if (self.parameters.loading) {
                console.log('Hide loading....');
            }
        }
    };
    this.send = function (type) {
        self = this;
        if (!navigator.userAgent.match(/iPad|iPhone|iPod|android|blackberry|IEMobile/i)) {
            this.loadingIcon.show();

            if (this.isUndefined(this.parameters.dataType)) this.parameters.dataType = 'json';
            if (this.isUndefined(this.parameters.contentType)) this.parameters.contentType = 'application/json';
            if (this.isUndefined(this.parameters.loading)) this.parameters.loading = false;
            if (this.isUndefined(this.parameters.crossDomain)) this.parameters.crossDomain = true;
            if (this.isUndefined(this.parameters.async)) this.parameters.async = true;
            if (this.isUndefined(this.parameters.timeout)) this.parameters.timeout = 30000;
            if (this.isUndefined(this.callback.error)) this.callback.error = this.defaultError;

            return $.ajax({
                type: type,
                url: this.parameters.url,
                dataType: this.parameters.dataType,
                data: JSON.stringify(this.parameters.data),
                contentType: this.parameters.contentType,
                crossDomain: this.parameters.crossDomain,
                async: this.parameters.async,
                timeout: this.parameters.timeout,
                success: function (data) {
                    var args = arguments[2];
                    if (!self.isUndefined(self.callback.success)) {
                        if (self.isUndefined(self.parameters.otherData))
                            self.callback.success.call(this, data, args.statusText, args);
                        else
                            self.callback.success.call(this, data, args.statusText, args, self.parameters.otherData);
                    }
                    self.loadingIcon.hide();
                },
                error: function (xhr, ajaxOptions, thrownError) {
                    xhrServerObj = xhr;
                    self.callback.error.call(xhr, ajaxOptions, thrownError);
                    self.loadingIcon.hide();
                }
            });
        }
    }
}

使用下面调用

var aj1 = new MyAjax();
    aj1.parameters = { url: API + '/api/mobileapp/stores' };
    aj1.callback.success = successGetAllStores;
    aj1.get();

    var aj2 = new MyAjax();
    aj2.parameters = { url: API + '/api/mobileapp/categories' };
    aj2.callback.success = successGetAllCategory;
    aj2.get();

【问题讨论】:

  • 通过学习如何使用$.extend()为自己节省大量代码

标签: javascript jquery ajax


【解决方案1】:

JavaScript 中最恰当的面向对象开发利用函数的原型。

所以首先你创建一个函数:

Foo = function()
{
};

然后你将函数添加到它的原型中:

Foo.prototype.Blah = function()
{
   // here you can access 'this'
};

现在 Blah() 是名为 Foo 的对象/类的函数。您可以使用 new 关键字进行实例化并按预期调用函数:

var f = new Foo;
f.Blah();

原型也可以用于变量成员。 JavaScript 中有两种类型的变量成员:静态和非静态。如果您使用诸如 Google 的闭包编译器之类的编译器,您还可以假装定义公共、受保护和私有成员,但在最终版本中,一切都是公共的。

Foo.static_value = 123;  // initialized on load, common to all objects
Foo.prototype.value = 5; // initialized to 5 on construction

在大多数情况下,您希望在静态变量为常量时使用它们。

但是,变量成员存在一个陷阱。如果您定义了一个对象或数组,这些不会被复制,因此相同的引用将出现在所有对象中。在大多数情况下,以下是错误的:

Foo.prototype.ary = [1, 2, 3];
Foo.prototype.obj = { a: 1, b: 2, c: 3 };

解决这个问题的方法是在构造函数中初始化数组和对象,如下所示:

Foo = function()
{
  this.ary = [1, 2, 3];
  this.obj = { a: 1, b: 2, c: 3 };
};
Foo.prototype.Blah = function()
{
   this.ary.push(17); // not common to all objects of type Foo
};

那么这些成员的声明使用null:

Foo.prototype.ary = null;
Foo.prototype.obj = null;

这样您就避免了引用另一个对象的问题,这对您的所有对象都是通用的。

您可能还想使用名称空间,它只是一个变量,以避免以后出现潜在问题。假设你的库是 call lib,你可以这样写:

var lib = {};

然后将其添加到所有 Foo 定义中,如下所示:

lib.Foo = function() { ... };

还有一些方法可以从另一个对象派生出一个对象(“扩展”),但这更先进!

【讨论】:

    【解决方案2】:

    认为我可以看到您的问题是什么 - 您使用相同的对象实例 (ajax) 作为每次调用 object.create 的原型(因此它们使用共享资源) - 所以后续调用相同东西互相跺脚。

    您可以在执行 object.create 时尝试使用 $.extend:

    var clonedAjax = $.extend(true, {}, ajax);
    var req = Object.create(clonedAjax);
    

    但是 - 处理此代码的一种更简洁的方法是将 ajax 作为一个类使用,而根本不使用 object.create。

    function ajax(){
        this.parameters = {
            ....
        };
        this.get = function(){};
        .....
    }
    

    然后

    var req = new ajax();
    

    更新: 正常工作:从外观上看,这是一个范围问题,这是您的方法的一个 CUT DOWN 工作示例 - 关键是在您发送和使用时分配“scb”在“成功”期间。 如果这不值得打绿勾,那就什么都做;)

    var ajax = {
        parameters: {
            url: '',
            dataType: 'json',
            data: {},
            contentType: 'application/json',
            loading: false,
            crossDomain: true,
            async: true,
            timeout: 30000,
            otherData: {}
        },
        callback: {
            success: null,
            error: null
        },
        get: function ()
        {
            this.send('GET');
        },
        post: function ()
        {
            this.send('POST');
        },
        del: function ()
        {
            this.send('DELETE');
        },
        send: function (type)
        {
            self = this;
            if (!navigator.userAgent.match(/iPad|iPhone|iPod|android|blackberry|IEMobile/i))
            {
    
                var scb = self.callback.success; // at the point where you SEND, make a note of the callback's that are attached and use during success
                //note - the callbacks that are attached when the send succeeds / fails can be different!
    
    
                return $.ajax({
                    type: type,
                    url: this.parameters.url,
                    dataType: this.parameters.dataType,
                    data: JSON.stringify(this.parameters.data),
                    contentType: this.parameters.contentType,
                    crossDomain: this.parameters.crossDomain,
                    async: this.parameters.async,
                    timeout: this.parameters.timeout,
                    success: function (data)
                    {
                        var args = arguments[2];
                        if (scb)
                        {
                            if (!self.parameters.otherData)
                                scb.call(this, data, args.statusText, args);
                            else
                                scb.call(this, data, args.statusText, args, self.parameters.otherData);
                        }
                    },
                    error: function (xhr, ajaxOptions, thrownError)
                    {
                        xhrServerObj = xhr;
                    }
                });
            }
        }
    }
    
    var get1 = Object.create(ajax);
    var get2 = Object.create(ajax);
    
    getGet(get1, cb1);
    getGet(get2, cb2);
    
    function getGet(req, cb)
    {
        req.parameters = { url: "Req" };
        req.callback.success = cb;
        req.get();
    }
    
    function cb1()
    {
        console.log("cb1");
    }
    function cb2()
    {
        console.log("cb2");
    }
    

    【讨论】:

    • 感谢您的解释。我已经尝试了这两种方法,但它不工作。 1. 使用 $.extend 我收到错误“无法设置未定义的属性'成功'”。 2. 使用类函数,其行为与上面的原型对象相同,覆盖了最新的回调函数。
    • 已更新以包含一个工作示例 - 如果您不介意一些建议 - 请阅读 jquery 承诺、$.extend 以及评估是否存在某些内容时的真/假与否(你根本不需要那个“IsUndefined”函数)。此外,如果您想使用这种方法,我建议使用 IEFE 声明“ajax”并关闭参数,更好的是,使用类方法(函数 ajax(){})而不是声明原型和使用 object.create。 HTH!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多