【问题标题】:Return additional values along with promise与承诺一起返回附加值
【发布时间】:2020-01-25 12:01:55
【问题描述】:

我有一个主表单和多个相关的内联表单。提交主表单时,应同时提交所有其他内联表单。

但是,如果任何内联表单失败(我们在响应中对其进行标记),则不会汇总主表单。

问题是submitInlineForm 返回了一个承诺。如何传递表单提交是否成功,以便在submitForm处理?

let submitForm = $form => {
    let formsToSubmit = [];
    $('.form-inline').each(function () {
        const $f= $(this);
        if (submittable($f)) formsToSubmit.push(submitInlineForm($f));
    });

    $.when(formsToSubmit).done(() => {
        if (!allFormsSuccessfull) return false;  // how to determine all forms were successfull?
        $.ajax({...});
    })
};


let submitInlineForm = $form => {
    return $.ajax({
        url: $form.attr('action'),
        type: 'post',
        success: response => {
            if (response.success) {
                // ???????
                // what to do here to indicate the inline form was successfully submitted?
            } else {
                // ???????
            }
        }
    });
};

【问题讨论】:

    标签: javascript jquery ajax promise


    【解决方案1】:

    好问题,但可能不像最初看到的那么简单。

    首先,您不需要与承诺一起返回额外的值。

    除非出现同步错误,否则表单提交的全部成功/失败都会在 submitInlineForm() 返回的承诺中传达……但其中存在缺陷……

    • jQuery.ajax() 返回的承诺是非标准的。
    • jQuery.when()Promise.all() 聚合的一组promise 中的任何单一故障都将(不采取措施防止它)导致聚合promise 走上错误路径。

    ...所以:

    • 为了计算成功和失败你可以reflectsubmitInlineForm()返回的promise,否则任何一个失败都会导致Promise.all()返回的promise失败。
    • 为了让 jqXHR 成功反映(以标准方式),您需要首先规范化暴露给 jqXHR 的.then() 回调的多个参数。

    幸运的是,有几个实用函数可以让主代码非常简单。

    let reflect = (promise) => {
        return promise.then(function(v){ return {v:v, status: "fulfilled" }},
                            function(e){ return {e:e, status: "rejected" }});
    };
    
    let normalise = (jqXHR) => {
        // Recastast jqXHR as native js Promise by wrapping in `Promise.resolve()`.
        return Promise.resolve(jqXHR.then((response, textStatus, jqXHR) => {
            // this is a jQuery success handler.
            if (response.success) {
                // normalise the params into one js plain object
                return { response, textStatus, jqXHR };
            } else {
                return $.Deferred().reject(new Error('response.success was falsy')); // throw the jQuery way (not strictly necessary in jQuery v3+)
            }
        }, (jqXHR, textStatus, errorThrown) => {
            // This is a jQuery error handler.
            // Normalise the params into one js Error object.
            return $.Deferred().reject(new Error(textStatus || errorThrown)); // throw the jQuery way (not strictly necessary in jQuery v3+)
        }));
    };
    

    有了实用程序,主要代码就很简单了。

    let submitForm = () => {
        let formsToSubmit = $('.form-inline').get().filter(submittable);
    
        return Promise.all(formsToSubmit.map(submitInlineForm).map(normalise).map(reflect)) // Yay, the two pitfalls are overcome in a single line!
        .then(outcomes => {
            // Because all the promises in formsToSubmit were reflected, you will end up here regardless of any error(s).
            // Now separate out the successes and errors
            let successes = outcomes.filter(o => o.status === 'fulfilled').map(o => o.v.response); // array of raw ajax responses
            // let successes = outcomes.filter(o => o.status === 'fulfilled').map(o => o.v.response.values); // alternatively, you might choose to extract the data of interest fom the raw responses at this point.
            let errors = outcomes.filter(o => o.status === 'rejected').map(o => o.e); // array of errors
            if (errors.length > 0) { // were there any errors?
                return false;
            } else {
                // All forms were successfull (`successes` should be congruous with `outcomes`).
                // Do something with the successes.
                return $.ajax({...}); // or whatever
            }
        });
    };
    
    let submitInlineForm = $form => {
        return $.ajax({
            'url': $form.attr('action'),
            'type': 'post'
        });
    };
    

    未经测试

    【讨论】:

    • 请注意,这不是一个完整的通用解决方案。在 normalise() 中,测试 if(response.success) 是针对这个 SO 问题的。
    猜你喜欢
    • 2015-11-27
    • 1970-01-01
    • 1970-01-01
    • 2015-06-05
    • 2015-10-30
    • 2016-10-11
    • 1970-01-01
    • 2016-06-15
    相关资源
    最近更新 更多