【问题标题】:Use jQuery.ajax deferred with typescript and auto response handling使用带有打字稿和自动响应处理延迟的 jQuery.ajax
【发布时间】:2015-04-21 14:53:06
【问题描述】:

我对 Promises、deferred 和所有类似的东西非常陌生,我正在尝试使用 jQuery 将我的旧习惯(回调地狱)改变为 Promises(我知道它不尊重 Promise A+,但那是不是重点)。

我现在所做的是两者兼而有之,我正试图摆脱回调。我也在使用 TypeScript,但据我所知,它不应该相关。我只是给出公平的警告,代码不是纯 JS。

// TODO I provide the "done" and "fail" callback here, but I'd like to use .done and .fail instead, but I want them to be executed AFTER the automatic response handling.
WidgetContext.getContext(
    function(data){
        console.log(data)
    },
    function(error){
        console.log(error)
    }
);

// In WidgetContext class, TODO here I want to get rid of the callbacks as well.
public static getContext(done: any, fail: any): JQueryPromise<WidgetContext> {
    return Payline.WebService.WidgetWSProxy.ajax(
        'context1.json',
        {
            userId: 123456
        },
        done,
        fail
    );
}

// In WidgetWSProxy class, TODO here again, there should not be any callback.
public static ajax(method: string = '', data: any = {}, done?: any, fail?: any, options: JQueryAjaxSettings = WidgetWSProxy._defaultOptions): JQueryPromise<WidgetWSProxy>{
    var url = WidgetWSProxy.buildUrl(WidgetWSProxy._url, method);

    return WidgetWSProxy._ajax(url, data, done, fail, WidgetWSProxy._processAjaxResponseData, WidgetWSProxy._logId + ' ' +  url, options);
}

// In AbstractHttpProxy class, TODO the only callback should be the responseHandler.
protected static _ajax(url: string, data: any, done?: any, fail?: any, responseHandler?: any, logId: string = AbstractHttpProxy._logId, options: JQueryAjaxSettings = AbstractHttpProxy._defaultOptions): JQueryPromise<AbstractHttpProxy>{
    // On log la requête.
    log.group(logId);
    log.time(logId);

    // Si aucun gestionnaire de réponse n'est correctement fourni, utilisation de celui par défaut.
    if(!_.isFunction(responseHandler)){
        responseHandler = AbstractHttpProxy._defaultResponseHandler;
    }

    // On injecte les data dans les options, on fait ainsi afin de cacher la logique jQuery pour ce paramètre particulier qui sera souvent utilisé.
    options = _.merge(options, {data: data});

    log.info('Requête HTTP envoyée: ' + JSON.stringify({
            url: url,
            options: options
        }));

    // On effectue l'appel ajax et on retourne une Promise jQuery.
    return $.ajax(url, options)
        // Succès
        .done(function(data){
            if(_.isFunction(done)){
                responseHandler(url, data, true, function(data){
                    // TODO HERE I execute the "done" callback inside the done() function, but I should not. I just need to call the responseHandler and update the data so the next call to ".done()" would use the updated data, even though I define it when I call the "WidgetContext.getContext()" method.
                    done(data);
                });
            }else{
                logMissingCallback(getCallerName());
            }
        })
        // Erreur (connexion interrompue, 404, 500, peu importe)
        .fail(function(error){
            if(_.isFunction(fail)){
                responseHandler(url, error, false, function(error){
                    // TODO Same stuff here, with the fail().
                    fail(error);
                });
            }else{
                logMissingCallback(getCallerName());
            }
        })
        // Sera toujours exécuté, peu importe ce qu'il se passe. (succès/erreur)
        .always(function(){
            log.timeEnd(logId);
            log.groupEnd();
        }
    );
}

我的目标是隐藏使用代理 (WidgetWSProxy) 背后的一些逻辑,自动记录所有请求并处理 HTTP 响应以根据需要格式化它们,然后使用 .done 延迟函数使用转换后的响应。

它在这里工作,但如果我这样做,它不会在.done 调用中记录更新的响应。

WidgetContext.getContext(
    function(data){
        console.log(data)
    },
    function(error){
        console.log(error)
    }
)
.done(function(data){
        console.log('init')
        console.log(data)
    });

使用多年的回调地狱思维方式很难摆脱......感谢您的帮助!

【问题讨论】:

    标签: javascript jquery ajax promise jquery-deferred


    【解决方案1】:

    然后使用 .done 延迟函数使用转换后的响应。

    即使你使用的是 jQuery deferreds,你也应该习惯于总是并且只使用then,因为它确实让回调转换一个值 - 并为转换后的值返回一个可链接的承诺。

    使用了多年的回调地狱思维方式很难摆脱......

    • 从您的代码中删除所有成功/错误回调。全部。
    • 每个执行异步操作的函数(通常需要在最后调用一次的回调)都应该返回一个 Promise。

    这也将包括您的responseHandlers - 例如,如果它们是异步的,它们需要返回承诺,而不是接受回调。你的代码会变成

    WidgetContext.getContext().then(function(data){
        console.log(data)
    }, function(error){
        console.log(error)
    });
    
    public static getContext(): JQueryPromise<WidgetContext> {
        return Payline.WebService.WidgetWSProxy.ajax(
            'context1.json',
            {
                userId: 123456
            }
        );
    }
    
    public static ajax(method: string = '', data: any = {}, options: JQueryAjaxSettings = WidgetWSProxy._defaultOptions): JQueryPromise<WidgetWSProxy>{
        var url = WidgetWSProxy.buildUrl(WidgetWSProxy._url, method);
    
        return WidgetWSProxy._ajax(url, data, WidgetWSProxy._processAjaxResponseData, WidgetWSProxy._logId + ' ' +  url, options);
    }
    
    protected static _ajax(url: string, data: any, responseHandler?: any, logId: string = AbstractHttpProxy._logId, options: JQueryAjaxSettings = AbstractHttpProxy._defaultOptions): JQueryPromise<AbstractHttpProxy>{
        if(!_.isFunction(responseHandler))
            responseHandler = AbstractHttpProxy._defaultResponseHandler;
        options = _.merge(options, {data: data});
    
        log.group(logId);
        log.time(logId);
        log.info('Requête HTTP envoyée: ' + JSON.stringify({
            url: url,
            options: options
        }));
    
        return $.ajax(url, options).then(function(data){
            return responseHandler(url, data, true);
        }, function(error){
            return responseHandler(url, error, false);
        }).always(function(){
            log.timeEnd(logId);
            log.groupEnd();
        });
    }
    

    【讨论】:

    • 我认为then 等同于donefail,但它们在语义上更有意义。这不是真的吗?为什么不建议使用它们?
    • 因为they don't chain。通常,您会希望从您的函数返回一个承诺,并且done/fail 不会为转换后的值返回一个。诚然,有时您可以在链的末端使用它们。像 .always 这样返回原始承诺的东西也很有用。
    • 好的!那么donefail 是最后的电话吗?我应该只在链式调用的最后使用它们吗?
    • 是的,您可以像WidgetContext.getContext().done(console.log).fail(console.error); 一样使用它们。但是,始终使用then 更容易且不易出错
    • 如果done/then 方法不起作用,您可能在某处错过了return。或者,根据您的浏览器,当您将 log 方法作为回调传递时,您可能需要使用 console.log.bind(console)(或使用函数表达式)。
    猜你喜欢
    • 2015-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多