Solucionado (ver solução)
Solucionado
(ver solução)
9
respostas

O post dá certo, mas sempre cai do catch()

No método post() devido ao xhr.onreadystatechange ser chamado toda vez que a propriedade readyState é modificada, no post.html sempre será exibido a mensagem de erro com o xhr.responseText vazio.

Na comparação if(xhr.readyState == 4), as duas primeiras comparações são com o readyState igual a 2 e a 3, só após essas duas comparações é que ela vai ser igual a 4 e executando o resolve, porém como o reject foi executado 2 vezes, ele retorna o reject para o post.htm.

A solução que apliquei para este problema foi colocar no 'senão' o seguinte: - else if(xhr.status != 200){...}

Gostaria de saber se existe uma outra forma melhor de tratar esta situação.

Grato.

9 respostas

Bom dia.

Poste seu código completo para que eu e outras pessoas da plataforma possam ler.

Segue o código da forma que eu resolvi a situação

// Classe HttpService com o método post

class HttpService{
    post(url, dado){

        return new Promise((resolve, reject) => {

            let xhr = new XMLHttpRequest();

            xhr.open('POST', url, true);

            xhr.setRequestHeader("Content-type", "application/json");

            xhr.onreadystatechange = () => {

                /* 
                no final eh recebido 3 alerts, pois o estado fica 2, 3 e 4
                (1: requisicao recebida, 2: processando requisicao, 3: requisicao concluida)
                */
                if(xhr.readyState == 4){

                    if(xhr.status == 200){

                        console.log(`Certo: ${xhr.readyState}`)
                        resolve(JSON.parse(xhr.responseText));

                    }
                } else if(xhr.status != 200){

                    console.log(`errado: ${xhr.readyState}`)
                    reject(xhr.responseText);

                }
            }

            xhr.send(JSON.stringify(dado));
        });

    }
}

// Script no arquivo post.html

function sendPost(event) {

    event.preventDefault();
    console.log("Enviando post");

    //aqui você deve ler os dados do formulário
    //construir o json
    //enviar o XMLHttpRequest

    let $ = document.querySelector.bind(document);

    inputData = $('#data');
    inpuntQuantidade = $('#quantidade');
    inputValor = $('#valor');
    inputForm = $('.form');

    let negociacao = {
        data: inputData.value,
        quantidade: inpuntQuantidade.value,
        valor: inputValor.value
    };

    let http = new HttpService();

    http.post('/negociacoes', negociacao).then(() => {

        alert("Negociação enviada com sucesso");
        inputForm.reset();
        inputData.focus();
    }).catch(erro => {

        console.log(erro);
        alert(`Não foi possivel enviar a negociação: ${erro}`);
    });
}

Talvez eu não tenha entendido sua dúvida. Mas vamos ver o código solução que consta na Alura:

class HttpService {

    get(url) {

        return new Promise((resolve, reject) => {


            let xhr = new XMLHttpRequest();

            xhr.open('GET', url);

            // só vai fazer resolve ou reject se readyState == 4

            xhr.onreadystatechange = () => {

                if (xhr.readyState == 4) {

                    // se terminou, ainda assim precisamos saber se foi tudo certo, verificando o status. 
                    if (xhr.status == 200) {

                        resolve(JSON.parse(xhr.responseText));
                    } else {

                        reject(xhr.responseText);
                    }
                }
            };

            xhr.send();


        });
    }

    // código omitido 
}

A chamada de resolve e reject só será feita uma única vez e quando o xhr.readyState for igual a 4. Não há como ele fazer dois reject como você relata. Porque ele só chega ao estado 4 uma única vez durante o tempo de vida.

Da forma que você fez, há um problema. Você não estará fazendo o reject se o status é diferente de 200 quando readyState for 4. Aliás, esse é um problema comum enfrentando por quem esta começando a lidar com requisições assíncronas usando XMLHTTPREQUEST, que é esquecer de verificar se o código de status é diferente de 200 na resposta, pois a API pode ser executada com sucesso e enviar um status code diferente de 200, como é no caso de API's REST.

Fiz um teste aqui e tudo funcionou perfeito. Pude fazer quantos posts eu desejar que a mensagem de sucesso é exibida para o usuário. Inclusive baixei o projeto final do curso para verificar se eu havia cometido um erro na hora de digitar e esse também esta conforme o padrão.

Agora fiquei na dúvida, se o seu post foi para corrigir uma solução sua ou se foi para propor uma melhoria no gabarito.

Aguardo seu feedback meu aluno! Se eu não entendi sua dúvida, grite :)

Isso, o código do exercício ele chama uma única vez o resolve, mas o reject ele chama 2 vezes e não exibe a mensagem que o post foi concluído com sucesso. Acontece isso devido ao xhr.readyState mudar para o estado 2 e 3 para só então ficar 4 e efetuar o resolve. Mas como ele já passou pelo reject, o navegador entende que deu erro e ao invés dele entrar no "then()" e resetar o formulário ele entra no "catch()".

Para resolver essa situação eu coloquei assim no HttpService:

if(xhr.readyState == 4){

                    if(xhr.status == 200){

                        console.log(`Certo: ${xhr.readyState}`)
                        resolve(JSON.parse(xhr.responseText));

                    }
               /* aqui ele passava duas vezes com o xhr.readyState igual a 2 e a 3 
               executando o reject duas vezes para só então executar o resolve.
                O que faz que no meu post.html ele entre no catch() e não no then()*/
                } else if(xhr.status != 200){

                    console.log(`errado: ${xhr.readyState}`)
                    reject(xhr.responseText);

}

Espero que tenha entendido agora. Eu consegui resolver essa pequena inconsistência, porém gostaria de saber se existe uma forma melhor para resolver isso.

Tem como você postar o código que você esta se referindo? Porque o código que postei (é o que esta na Alura) o reject só é chamado uma única vez. Inclusive você pode testá-lo em sua máquina que funcionará. A lógica é: uma requisição só chega ao readyState 4 uma vez e ao chegar, testamos se o status é 200, se for, resolve, se não for reject.

Como indiquei, sua solução não lida com código de status diferente de 200 quando o readyState é 4.

Entendi, acabei de baixar a versão final do projeto e ele exibe a mensagem correta, porém no meu projeto é emitida a mensagem de erro, mas o post é executado corretamente. Vou comparar os dois projetos e vê o que eu errei.

Ah, entendi. Acredito que você tenha feito o exercício sem olhar o gabarito (o que é ótimo para fixar o conteúdo). Por isso a diferença.

Tá tranquilo, o importante é no final tudo funcionar.

Sucesso e bom estudo meu aluno!

solução!

Obrigado Flávio, encontrei o erro. Eu estava colocando o else na comparação xhr.readyState == 4 e no código final do projeto é colocado no xhr.status == 200, conforme abaixo.

Ou seja, o meu else estava no local errado... xD

if(xhr.readyState == 4){

    if(xhr.status == 200){

        console.log(`Certo: ${xhr.readyState}`)
        resolve(JSON.parse(xhr.responseText));

    } else{

        console.log(`errado: ${xhr.readyState}`);
        reject(xhr.responseText);
    }
}