即使我在所有 then-ables 中使用拒绝回调,我是否总是需要在最后使用 catch()?

Do I always need catch() at the end even if I use a reject callback in all then-ables?

我将捕获放在最后,但它们至少在一个特定实例中返回空对象。是否需要捕获任何不为人知的东西,还是只是把我搞砸了?

$( document).ready(function(){
    app.callAPI()//a chainable a RSVP wrapper around a jquery call, with its own success() fail() passing forward to the wrapper, so it will either be a resolved or rejected thenable to which is now going to be chained 
        .then(
            function(env) {
                //set the property you needed now
                app.someSpecialEnvObj = env;
            },
            function(rejectMessage){
                console.log('call.API() cant set some special env object..');
                console.log(rejectMessage);
            }
        )
        .catch(
        function(rejectMessage){
            if(rejectMessage){
                //a just incase, DOES IT HAVE VALUE, for somebody that may have not done their homework in the parent calls?
                console.log('you have some kind of legitimate error, maybe even in callAPI() that is not part of any problems inside them.  you may have forgotton handle something at an early state, your so lucky this is here!)
            } else {
                console.log('can this, and or will this ever run.  i.e., is there any value to it, when the necessity to already be logging is being handled in each and every then already, guaranteeing that we WONT be missing ANYTHING')
            }
        }
    );
});

有错吗?还是它有某种用途,即使我仍然在所有父链接的 then-ables 中对 .then(resolve, reject) 方法的所有用法使用 error/reject 处理程序?

编辑: 我希望有更好的代码示例。我想我可能仍在命名中使用某种反模式,我 rejectMessage 在我的例子中,它是 jqXhr 对象,对吗?

也许我应该准确地命名它们?即 jqXhr?顺便说一下,如果出现错误,我喜欢在每个 then() 内当场拒绝它的原因是因为这样我可以大量记录每个单独的呼叫,如果那里有具体问题,那样我不需要追踪任何东西。微日志,因为我可以。

Promises 正在帮助以这种方式打开调试世界。

这是我试过的三个例子。我更喜欢方法 1 和方法 2,我决不会回到方法 3,这是我在应许之地开始的地方。

//method 1
app.rsvpAjax = function (){
    var async,
        promise = new window.RSVP.Promise(function(resolve, reject){
            async = $.extend( true, {},app.ajax, {
                success: function(returnData) {
                    resolve(returnData);
                },
                error: function(jqXhr, textStatus, errorThrown){
                    console.log('async error');
                    console.log({jqXhr:  jqXhr, textStatus: textStatus, errorThrown: errorThrown});
                    reject({ jqXhr: jqXhr, textStatus: textStatus, errorThrown: errorThrown}); //source of promise catch data believe
                }
            });
            $.ajax(async); //make the call using jquery's ajax, passing it our reconstructed object, each and every time
        });
    return promise;
};

app.callAPI = function () {
    var promise =app.rsvpAjax();
    if ( !app.ajax.url ) {
        console.log("need ajax url");
        promise.reject(); //throw (reject now)
    }
    return promise;
};

//method 2
app.ajaxPromise = function(){
    var  promise,  url = app.ajax.url;
    var coreObj = { //our XMLHttpRequestwrapper object
        ajax : function (method, url, args) {  // Method that performs the ajax request
            promise = window.RSVP.Promise( function (resolve, reject) {    // Creating a promise
                var client = new XMLHttpRequest(),  // Instantiates the XMLHttpRequest
                    uri = url;
                uri = url;
                if (args && (method === 'POST' || method === 'PUT')) {
                    uri += '?';
                    var argcount = 0;
                    for (var key in args) {
                        if (args.hasOwnProperty(key)) {
                            if (argcount++) {
                                uri += '&';
                            }
                            uri += encodeURIComponent(key) + '=' + encodeURIComponent(args[key]);
                        }
                    }
                }
                client.open(method, uri);
                client.send();
                client.onload = function () {
                    if (this.status == 200) {
                        resolve(this.response);   // Performs the function "resolve" when this.status is equal to 200
                    }
                    else {
                        reject(this.statusText); // Performs the function "reject" when this.status is different than 200
                    }
                };

                client.onerror = function () {
                    reject(this.statusText);
                };
            });
            return promise;   // Return the promise
        }
    };
    // Adapter pattern
    return {
        'get' : function(args) {
            return coreObj.ajax('GET', url, args);
        },
        'post' : function(args) {
            return coreObj.ajax('POST', url, args);
        },
        'put' : function(args) {
            return coreObj.ajax('PUT', url, args);
        },
        'delete' : function(args) {
            return coreObj.ajax('DELETE', url, args);
        }
    };
};

app.callAPI = function () {
    var async, callback;
    async =app.ajaxPromise() ; //app.ajaxPromise() is what creates the RSVP PROMISE HERE<
    if(app.ajax.type === 'GET'){async = async.get();}
    else if(app.ajax.type === 'POST') {async = async.post();}
    else if(app.ajax.type === 'PUT'){async = async.put();}
    else if(app.ajax.type === 'DELETE'){ async = async.delete();}
    callback = {
        success: function (data) {
            return JSON.parse(data);
        },
        error: function (reason) {
            console.log('something went wrong here');
            console.log(reason);
        }
    };
    async = async.then(callback.success)
        .catch(callback.error);
    return async;
};

//method 3 using old school jquery deferreds
app.callAPI = function () {
    //use $.Deferred instead of RSVP
    async = $.ajax( app.ajax) //run the ajax call now
        .then(
        function (asyncData) { //call has been completed, do something now
            return asyncData;  //modify data if needed, then return, sweet success
        },
        function(rejectMessage) {  //call failed miserably, log this thing
            console.log('Unexpected error inside the callApi.  There was a fail in the $.Deferred ajax call');
            return rejectMessage;
        }
    );
    return async;
};

我也在某处运行这个onready作为另一个备份。

window.RSVP.on('error', function(error) {
    window.console.assert(false, error);
    var response;
    if(error.jqXhr){
        response = error.jqXhr;
    } else {
        //response = error;
        response = 'is this working yet?';
    }
    console.log('rsvp_on_error_report')
    console.log(response);
});

编辑 错误示例

//one weird error I can't understand, an empty string("")?
{
  "jqXhr": {
    "responseText": {
      "readyState": 0,
      "responseText": "",
      "status": 0,
      "statusText": "error"
    },
    "statusText": "error",
    "status": 0
  },
  "textStatus": "error",
  "errorThrown": "\"\""
}
//another wierd one, but this one comes from a different stream,  the RSVP.on('error') function
{
  "readyState": 0,
  "responseText": "",
  "status": 0,
  "statusText": "error"
}

如果我没记错的话,catch 会在你的 promise 被拒绝时触发。由于您附加了失败回调,除非您在失败或成功回调中调用 reject 函数,否则您的 catch 不会触发。 换句话说,catch 块正在捕获您的 then 方法中的拒绝。

I am putting catches at the end

这是他们的典型职位 - 您处理链中某处发生的所有错误。绝对不要忘记处理错误,这一点很重要,建议的做法是在最后包罗万象。

even if I use onreject handlers in all .then(…) calls?

这有点奇怪。通常所有错误都在中央位置处理(最后的 catch),但当然如果你愿意,你可以在任何地方处理它们,然后继续链。

只要确保理解difference between an onreject handler in a then and in a catch,您就可以自由使用它们。不过,最后的 catch 建议在 then 回调本身中捕获错误。

they are returning empty object in one particular instance atleast.

然后承诺就搞砸了——它不应该无缘无故地拒绝。似乎是

if ( !app.ajax.url ) {
    console.log("need ajax url");
    promise.reject();
}

在你的代码中应该是

if (!app.ajax.url)
    return Promise.reject("need ajax url");

Is a catch necessary for anything unbeknownst?

不是真的。问题是 catch 通常是一个包罗万象的东西,甚至可以捕获意外异常。那么如果你能分辨出来,那些意想不到的你会怎么办?

通常你会为这些设置某种全局 ,这样你就不必确保在每个承诺链的末尾手动处理它们。

我认为一般的问题应该在没有例子的情况下得到一个简单的答案。

迂腐的技术答案是'no',因为.catch(e)等同于.then(null, e)

不过(而且这是一个很大的"however"),除非你实际上 传入 null,你将传入一些在运行时可能会失败的东西(比如编码错误),并且你需要一个后续的 catch 来捕捉它,因为兄弟拒绝处理程序按照设计不会捕捉到它:

.then(onSuccess, onFailure); // onFailure wont catch onSuccess failing!!

如果这是链的尾部,那么 onSuccess 中的(甚至编码)错误将被永远吞没。所以不要那样做。

所以真正的答案是肯定的,除非你要退回链条,在这种情况下不会。

所有链都应终止,但如果您的代码只是调用者将在调用后添加的更大链的一部分 returns,则由调用者正确终止它(除非你的功能被设计成永不失败)。

我遵循的规则是:所有链必须返回或终止(使用 catch)。