function onError(res, apiMethod, params) {
    res.data = res.data || {};

    normalizeResponse(res);

    switch (res.status) {
        // Wrong headers
        case 0:
            res.data.errorMessage = ErrorReasons.WrongHeader.description;
            Toast.notify(res.data.errorMessage, res.data.errors).show();

            return Promise.reject(ErrorReasons.WrongHeader);

        // Bad Request
        case 400:
            // Error handling should be processed by request caller.
            // Common error message for this case
            res.data.errorMessage = ErrorReasons.BadRequest.description;

            return Promise.reject(res);

        // Unauthorized
        case 401:
            res.data.errorMessage = ErrorReasons.Unauthorized.description;
            Toast.notify(res.data.errorMessage, res.data.errors).show();

            return Promise.reject(ErrorReasons.Unauthorized);

        // Forbidden
        case 403:
            return Toast.prompt().show();

        // Internal Server Error
        case 500:
            return new Promise((resolve, reject) => {
                Toast.prompt(res.data.errors)
                    .show()
                    .then(() => {
                        return apiMethod(params)
                            .then((val) => {
                                resolve(val);
                            })
                            .catch((res) => {
                                return onError(res, apiMethod, params)
                                    .then(resolve)
                                    .catch(reject);
                            });
                    })
                    .catch(() => reject(res));
            });

        // Not Found
        case 404:
            return new Promise(function(resolve, reject) {
                res.data.errorMessage = ErrorReasons.NotFound.description;
                Toast.notify(res.data.errorMessage, res.data.errors).show();

                reject(res);
            });
    }
}

Example 1

function onError(res, apiMethod, params) {
    res.data = res.data || {};

    normalizeResponse(res);

    switch (res.status) {
        // Wrong headers
        case 0:
            return handleWrongHeaders({res});
        // Bad Request
        case 400:
            return handleBadRequest({res});
        // Unauthorized
        case 401:
            return handleUnauthorized({res});
        // Forbidden
        case 403:
            return handleForbidden();
        // Internal Server Error
        case 500:
            return handleInternalServerError({res, apiMethod, params});
        // Not Found
        case 404:
            return handleNotFound({res});
    }
}

function handleWrongHeaders({res}) {
    res.data.errorMessage = ErrorReasons.WrongHeader.description;
    Toast.notify(res.data.errorMessage, res.data.errors).show();

    return Promise.reject(ErrorReasons.WrongHeader);
}

function handleBadRequest({res}) {
    // Error handling should be processed by request caller.
    // Common error message for this case
    res.data.errorMessage = ErrorReasons.BadRequest.description;

    return Promise.reject(res);
}

function handleUnauthorized({res}) {
    res.data.errorMessage = ErrorReasons.Unauthorized.description;
    Toast.notify(res.data.errorMessage, res.data.errors).show();

    return Promise.reject(ErrorReasons.Unauthorized);
}

function handleForbidden() {
    return Toast.prompt().show();
}

function handleInternalServerError({res, params, apiMethod}) {
    return new Promise((resolve, reject) => {
        Toast.prompt(res.data.errors)
            .show()
            .then(() => {
                return apiMethod(params)
                    .then((val) => {
                        resolve(val);
                    })
                    .catch((res) => {
                        return onError(res, apiMethod, params)
                            .then(resolve)
                            .catch(reject);
                    });
            })
            .catch(() => reject(res));
    });
}

function handleNotFound({res}) {
    return new Promise(function(resolve, reject) {
        res.data.errorMessage = ErrorReasons.NotFound.description;
        Toast.notify(res.data.errorMessage, res.data.errors).show();

        reject(res);
    });
}

Blocks Are Extracted

const STATUS = {
    WRONG_HEADERS: 0,
    BAD_REQUEST: 400,
    UNAUTHORIZED: 401,
    FORBIDDEN: 403,
    INTERNAL_SERVER_ERROR: 500,
    NOT_FOUND: 404
};

function onError(res, apiMethod, params) {
    // res.data = res.data || {};
    // has gone into normalizeResponse, seems reasonable place for normalization
    normalizeResponse(res);

    switch (res.status) {
        case STATUS.WRONG_HEADERS:
            return handleWrongHeaders({res});

        case STATUS.BAD_REQUEST:
            return handleBadRequest({res});

        case STATUS.UNAUTHORIZED:
            return handleUnauthorized({res});

        case STATUS.FORBIDDEN:
            return handleForbidden();

        case STATUS.INTERNAL_SERVER_ERROR:
            return handleInternalServerError({res, apiMethod, params});

        case STATUS.NOT_FOUND:
            return handleNotFound({res});
    }
}

function handleWrongHeaders({res}) {
    res.data.errorMessage = ErrorReasons.WrongHeader.description;
    Toast.notify(res.data.errorMessage, res.data.errors).show();

    return Promise.reject(ErrorReasons.WrongHeader);
}

function handleBadRequest({res}) {
    // Error handling should be processed by request caller.
    // Common error message for this case
    res.data.errorMessage = ErrorReasons.BadRequest.description;

    return Promise.reject(res);
}

function handleUnauthorized({res}) {
    res.data.errorMessage = ErrorReasons.Unauthorized.description;
    Toast.notify(res.data.errorMessage, res.data.errors).show();

    return Promise.reject(ErrorReasons.Unauthorized);
}

function handleForbidden() {
    return Toast.prompt().show();
}

function handleInternalServerError({res, params, apiMethod}) {
    return new Promise((resolve, reject) => {
        Toast.prompt(res.data.errors)
            .show()
            .then(() => {
                return apiMethod(params)
                    .then((val) => {
                        resolve(val);
                    })
                    .catch((res) => {
                        return onError(res, apiMethod, params)
                            .then(resolve)
                            .catch(reject);
                    });
            })
            .catch(() => reject(res));
    });
}

function handleNotFound({res}) {
    return new Promise(function(resolve, reject) {
        res.data.errorMessage = ErrorReasons.NotFound.description;
        Toast.notify(res.data.errorMessage, res.data.errors).show();

        reject(res);
    });
}

Removed magic numbers

Removed redundant comments

const STATUS = {
    WRONG_HEADERS: 0,
    BAD_REQUEST: 400,
    UNAUTHORIZED: 401,
    FORBIDDEN: 403,
    INTERNAL_SERVER_ERROR: 500,
    NOT_FOUND: 404
};

function onError(res, apiMethod, params) {
    normalizeResponse(res);

    const status = res.status;
    const handlersMap = getDefaultHandlers();

    if (handlersMap.hasOwnProperty(status)) {
        return handlersMap[status]({res, apiMethod, params})
    }
}

function getDefaultHandlers() {
    return {
        [STATUS.WRONG_HEADERS]: handleWrongHeaders,
        [STATUS.BAD_REQUEST]: handleBadRequest,
        [STATUS.UNAUTHORIZED]: handleUnauthorized,
        [STATUS.FORBIDDEN]: handleForbidden,
        [STATUS.INTERNAL_SERVER_ERROR]: handleInternalServerError,
        [STATUS.NOT_FOUND]: handleNotFound
    };
}

function handleWrongHeaders({res}) {
    res.data.errorMessage = ErrorReasons.WrongHeader.description;
    Toast.notify(res.data.errorMessage, res.data.errors).show();

    return Promise.reject(ErrorReasons.WrongHeader);
}

function handleBadRequest({res}) {
    // Error handling should be processed by request caller.
    // Common error message for this case
    res.data.errorMessage = ErrorReasons.BadRequest.description;

    return Promise.reject(res);
}

function handleUnauthorized({res}) {
    res.data.errorMessage = ErrorReasons.Unauthorized.description;
    Toast.notify(res.data.errorMessage, res.data.errors).show();

    return Promise.reject(ErrorReasons.Unauthorized);
}

function handleForbidden() {
    return Toast.prompt().show();
}

function handleInternalServerError({res, params, apiMethod}) {
    return new Promise((resolve, reject) => {
        Toast.prompt(res.data.errors)
            .show()
            .then(() => {
                return apiMethod(params)
                    .then((val) => {
                        resolve(val);
                    })
                    .catch((res) => {
                        return onError(res, apiMethod, params)
                            .then(resolve)
                            .catch(reject);
                    });
            })
            .catch(() => reject(res));
    });
}

function handleNotFound({res}) {
    return new Promise(function(resolve, reject) {
        res.data.errorMessage = ErrorReasons.NotFound.description;
        Toast.notify(res.data.errorMessage, res.data.errors).show();

        reject(res);
    });
}

Extract handlers

function onError(res, apiMethod, params, handlers = {}) {
    const handlersMap = {
        ...getDefaultHandlers(),
        ...handlers
    };

    const {status} = res;

    if (handlersMap.hasOwnProperty(status)) {
        return handlersMap[status]({res, apiMethod, params})
    }
}

Is it one responsibility?

function onError(res, apiMethod, params) {
    const handlersMap = getDefaultHandlers();

    const {status} = res;
    const handle = getHandlerByStatus(handlersMap, status);
    return handle({res, apiMethod, params});
}

function getHandlerByStatus(handlersMap, status) {
    if (handlersMap.hasOwnProperty(status)) {
        return handlersMap[status];
    }

    if (handlersMap.hasOwnProperty(STATUS.DEFAULT)) {
        return handlersMap[STATUS.DEFAULT];
    }

    return noop;
}
function onError(res, apiMethod, params, handlers) {
    const {status} = res;
    const handle = getHandlerByStatus(handlers, status);
    return handle({res, apiMethod, params});
}

Table Method example

By Semyon Radionov