包装jQuery.ajax:将内部jqXHR级联为要返回的新的deffered对象

时间:2015-09-28 02:27:03

标签: javascript jquery ajax promise jquery-deferred

我现在正在使用jQuery插件开发,我希望在$.ajax发送之前进行一些预处理操作:

// The signature is the same with $.ajax
$.myAjax = function(url, options) {


    var data = options.data;
    var promises = [];

    for(var name in data) {
        if(data.hasOwnProerty(name)) {
            var val = data[name];
            if(val instanceof File) {

                // I want to do some async pre-process here.
                var dfd = $.Deferred();

                var reader = new FileReader();
                reader.onload = function(e) {
                    data.name = e.target.result;
                    dfd.resolve();
                }
                reader.readAsText(val);

                promises.push(dfd.promise());
            }
        }
    }

    var deferred = $.Deferred();

    $.when.apply($, promises).done(function() {
        // In fact, I want to return, or wrap cascading this jqXHR
        //   in the outer function `$.myAjax`.
        var jqXHR = $.ajax(url, options).done(function(...) {
            // ??? If I want deferred to be a jqXHR like object, 
            // how to wrap the arguments here?
            deferred.resolve(/* Help to fill */); 
        }).fail(function(...) {
            deferred.reject(/* Help to fill */);
        });
    });

    // ** ATTENTION **
    // Here, I want to return a jqXHR compatible promise.
    // That is what I ask here.
    return deferred.promise();

}

我希望在myAjax中返回一个Deferred对象,或者更确切地说,返回一个jqXHR对象。

这样我就可以使用标准$.ajax方法调用完全相同的接口:

$.fn.myAjax({...}).done(function(data, textStatus, jqXHR) {
    // ...
}).fail(function(jqXHR, textStatus, errorThrown) {
    // ...
}) 
// .always ... etc.

2 个答案:

答案 0 :(得分:4)

如果我正确理解你要做的事情,那就无法做到。问题是您的代码在创建jqXHR对象之前从$.myAjax()返回,因此jqXHR对象无法成为$.myAjax()函数调用的实际返回对象。您可以从返回的promise中访问它,但返回的promise将是您在ajax调用开始之前创建的一个promise。

仅供参考,您在代码中也有一些承诺反模式,因为您从$.ajax()处理程序返回$.when()而不是使用您创建的其他延迟。从.then()处理程序中返回承诺会自动将承诺链接到原始承诺。

这是您发布的解决方案的清理版本。变更摘要:

  1. 将文件读取封装到本地函数中以避免在循环中声明函数并允许所有核心逻辑流仅使用promises而不是promises和callback的混合(例如封装回调)。
  2. 向文件阅读器添加了错误处理
  3. 将延迟切换到回调模型(std promises使用)
  4. 删除了deferred anti-pattern,而只是返回ajax承诺,它将根据需要从ajax调用中获得解析或拒绝的参数
  5. 切换到具有更标准行为的.then(),当jQuery使其承诺标准符合时,不需要更改
  6. 代码:

    // The signature is the same with $.ajax
    $.myAjax = function(url, options) {
    
        function readFile(data, name) {
            var file = data[name];
            if (file instanceof File) {
                return $.Deferred(function(dfd) {
                    var reader = new FileReader();
                    reader.onload = function(e) {
                        dfd.resolve(e.target.result);
                        data[name] = e.target.result;
                    };
                    reader.onerror = reader.onabort = dfd.reject;
                    reader.readAsText(file);
    
                }).promise();
            }
        }
    
        var data = options.data;
        var promises = [];
    
        for(var name in data) {
            if(data.hasOwnProerty(name)) {
                promises.push(readFile(data, name));
            }
        }
    
        // trigger when all file fields was loaded.
        // so the data were all constructed.
        return $.when.apply($, promises).then(function() {
            return $.ajax(url, options);
        });
    }
    

答案 1 :(得分:-1)

我最终找到解决方案:

最后,我尝试推断resolveWithrejectWith jqXHR.done()jqXHR.fail()签名的jqXHR.done(function( data, textStatus, jqXHR ) {}); jqXHR.fail(function( jqXHR, textStatus, errorThrown ) {}); jqXHR.always(function( data|jqXHR, textStatus, jqXHR|errorThrown ) {}); // The signature is the same with $.ajax $.myAjax = function(url, options) { var data = options.data; var promises = []; for(var name in data) { if(data.hasOwnProerty(name)) { var val = data[name]; if(val instanceof File) { (function(name, val) { // Deferred for a single field loaded. var dfd = $.Deferred(); var reader = new FileReader(); reader.onload = function(e) { data[name] = e.target.result; dfd.resolve(); } reader.readAsText(val); promises.push(dfd.promise()); })(name, val); } } } // Overall deferred to cascading jqXHR from ajax. // with returning the same argument list. var deferred = $.Deferred(); // resolveWith or rejectWith requires a context. // Thought from the jQuery ajax source code. var callbackContext = options.context || options; // trigger when all file fields was loaded. // so the data were all constructed. $.when.apply($, promises).done(function() { // ********** FINAL SOLUTION ********** $.ajax(url, options).done( function(data, textStatus, jqXHR) { deferred.resolveWith(context, [data, textStatus, jqXHR]); }).fail( function(jqXHR, textStatus, errorThrown) { deferred.rejectWith(context, [jqXHR, textStatus, errorThrown]); }); }); // So that the resulting promise is well constructed. return deferred.promise(); } 相同的参数列表。

签名参考:http://api.jquery.com/jQuery.ajax/#jqXHR

$.myAjax

所以整体解决方案是:

$.ajax

所以,现在我们可以使用var dfd = $.myAjax({ url: '...', // ... }).done(function(data, textStatus, jqXHR) { // triggered when the inner ajax done ... }).fail(function(jqXHR, textStatus, errorThrown) { // triggered when the inner ajax fail ... }); 功能与Error: Copying file UserUploads\myfiles\Uploads\Images\DSC_2987.jpg to obj\Release\Package\PackageTmp\UserUploads\myfiles\Uploads\Images\DSC_2987.jpg failed. Could not find file 'UserUploads\myfiles\Uploads\Images\DSC_2987.jpg'. CivilContractorMVCApp 0 0 IntelliSense 1 相同:

type
  TOnMyExecute = procedure( Sender : TObject ) of object; // sender not required here but is convention

  TMyThread = class( TThread )
    private 
      fMyOnExecute : TOnMyExecute;
    public
    procedure Execute(); override; // inside this I need to call TASKForm.MonitorA.somemethod, TASKForm.ConstB.somemethod and TASKForm.calculateStmp
    property MyOnExecute : TMyOnExecute read fMyOnExecute write fMyOnExecute;
  end;