Всегда ли мне нужен catch() в конце, даже если я использую обратный вызов во всех возможных состояниях?

Я ставлю ловушки в конце, но они возвращают пустой объект по крайней мере в одном конкретном случае. Нужен ли улов для чего-то незаметного, или он просто меня облажает?

$( 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(resolve, reject) методы во всех родительских цепочках?

РЕДАКТИРОВАТЬ: лучше пример кода, я надеюсь. Я думаю, что я все еще могу использовать какой-то анти-шаблон в именовании, я rejectMessage в моем, например, это объект jqXhr правильно?

Так, может быть, я должен назвать их именно так или как? т.е. jqXhr? Кстати, причина, по которой мне нравится отказываться от него на месте внутри каждого then()Если произошла ошибка, это потому, что таким образом я могу обильно регистрировать каждый отдельный вызов, если там была конкретная проблема, таким образом мне не нужно ничего отслеживать. Micro-logging, потому что я могу.

Обещания помогают открыть мир отладки таким образом.

Вот три примера, которые я попробовал. Я предпочитаю method1 и method2, и я ни в коем случае не возвращаюсь к method3, с которого я начал в земле обетованной.

//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"
}

3 ответа

Решение

Я ставлю уловы в конце

Это типичная для них позиция - вы обрабатываете все ошибки, которые произошли где-то в цепочке. Важно вообще не забывать обрабатывать ошибки, и в конечном итоге рекомендуется всеохватывающее решение.

даже если я использую onreject обработчики во всех .then(…) звонки?

Это немного странно. Обычно все ошибки обрабатываются в центральном месте (catch в конце), но, конечно, если вы хотите, вы можете обработать их где угодно, а затем продолжить цепочку.

Просто убедитесь, что вы понимаете разницу между onreject обработчик в then и в catch и вы можете использовать их свободно. Тем не менее, catch в конце рекомендуется ловить ошибки в then Сами обратные вызовы.

они возвращают пустой объект по крайней мере в одном конкретном случае.

Тогда обещание облажалось - оно никогда не должно отклоняться без причины. Кажется, будет вызвано

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

в вашем коде, который должен был быть

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

Нужен ли улов для чего-то без ведома?

На самом деле, нет. Проблема в том, что catch это обычно ловушка, даже ловить неожиданные исключения. Так что, если вы сможете их различить, что бы вы сделали с неожиданными?

Обычно вы устанавливаете какой-то глобальный необработанный обработчик отклонения для них, так что вам не нужно обязательно обрабатывать их вручную в конце каждой цепочки обещаний.

Я думаю, что общий вопрос заслуживает простого ответа без примера.

Педантичный технический ответ "нет", потому что .catch(e) эквивалентно .then(null, e),

Однако (и это большое "однако"), если вы на самом деле не проходите null, вы будете передавать что-то, что может произойти сбой при запуске (например, ошибка кодирования), и вам понадобится последующее catch чтобы поймать это, так как обработчик отклонения родного брака по дизайну не поймает его:

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

Если это хвост цепочки, то (даже кодирование) ошибки в onSuccess поглощены навсегда. Так что не делай этого.

Таким образом, настоящий ответ - да, если только вы не вернете цепочку, в этом случае нет.

Все цепочки должны быть прерваны, но если ваш код является лишь частью более крупной цепочки, к которой звонящий будет добавлять после того, как ваш вызов вернется, то звонящий сам должен завершить его должным образом (если ваша функция не предназначена для того, чтобы никогда не завершаться сбоем),

Правило, которое я соблюдаю: все цепочки должны быть либо возвращены, либо прекращены (с catch).

Если я правильно помню, загорается, когда ваше обещание будет отклонено. Поскольку у вас есть прикрепленный обратный вызов, ваш перехват не сработает, если вы не вызовете функцию отклонения в обратном вызове сбоя или успеха. Другими словами, блок catch обнаруживает отклонение в вашем методе then.

Другие вопросы по тегам