Opa tudo bem Bruno? Seguinte, eu tecnicamente aprendi async/await na marra sem ordem nem nada, não estou dizendo que é o certo. Portanto, a diferença entre promise e try/catch, funcionalmente são a mesma coisa, mas a promise se torna um pouco diferente.

Try/catch

Esse método, ele é utilizado em casos sincronos onde não há a utilização de async/await, mas nada diz que você precisa utilizar esse método só nessa situação. No exemplo abaixo explico como funciona o resultado da função.

async function hello_world() {
  try {
    const request = await _api-response_
    return request;
  } catch (error) {
      return error;
    }
}

Nessa função, o resultado produzido será um return com os dados esperados - se der certo - ou um resultado com os erros. Se o resultado for o esperado e você, por exemplo receber um json, você conseguirá acessar o json sem precisar realizar outro passo, nesse caso, um .then que é necessário quando se tem uma promise sem async/await.

Promise

Essencialmente a ideia é a mesma que o try/catch, mas esse método é utilizado e recomendado para funções assíncronas como em apis próprias. Normalmente se pode utilizar sem a necessidade async/await, mas você precisará encadear estruturas .then/.catch para saber os resultados.

function minhaFunçao() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("Hello");
        }, 2000);
    });

minhaFuncao()
    .then((result) => {
        console.log(result);
    })
    .catch((error) => {
        console.error(error);
    });

Neste exemplo, se você colocar a função (minhaFuncao) em um console, você perceberá que será mostrado: "Promise {<pending}". Nesse caso você não consegue saber o retorno da função, para saber você precisa realizar a estrutura de .then que mostra o resultado da promise. Entretanto, você pode evitar esse passos a mais utilizando o async/await para dizer ao request que você pode esperar as promises se resolverem e trazer o resultado esperado, sem a necessidade de fazer o .then/.catch.

Quando utilizar um ou outro?

Resumidamente, utiliza-se o método try/catch em situações síncronas que podem gerar erros que você queira tratar. Já a promise é utilizada, comumente, para funções assíncronas. Ou seja, essencialmente são a mesma coisa, são estruturas para tratar resultados diferentes, mas são utilizadas em contextos diferentes, como em contextos síncronos (try/catch) e assíncronos (promises). No fim, você pode utilizar try/catch em situações assícronas e promises em situações sincronas ou vice e versa, mas a utilizacão de promises em contextos assíncronos, torna a leitura do código mais limpa e clara demonstrando organização de código a outros programadores.

Complementando um pouco a resposta do EstevamOt, já que você tá começando a estudar funções assíncronas.

Quando a gente trabalha com Promise e Callbacks a ideia é que estamos avisando pro "código": olha, isso aqui pode demorar um pouco, então ao invés de bloquear a execução de tudo pode ir fazendo o que precisa e quando eu terminar eu te aviso.

Então pegando como exemplo a função:

/**
 * Imagina que é uma chamada a alguma API
 * 
 * @returns {Promise<number>}
 */
function pegaIdade() {
    return new Promise(function (resolve, reject) {
        const idade = Math.floor(Math.random() * 100);
        
        // Caso deu tudo certo
        if (idade) {
            resolve(idade);
        } else {
            reject("Idade Inválida");
        }
    });
}

A função pegaIdade faria alguma consulta a alguma API e retornaria uma Promise. A Promise aceita 2 funções de callback, uma que é executada quando a operação ocorre com sucesso (resolve) e outra para quando tem erro na operação (reject).

A Promise aceita, por meio de uma interface fluente, indicar o que será executado quando terminar. No caso de sucesso executará o then e no caso de falha pulará direto para o catch.

pegaIdade()
    .then(idade => console.log(idade))
    .catch(error => console.error(error))

O bom de usar Promise é que você pode executar todas de uma única vez e então ter só 1 callback pra executar após todas retornarem algo. Muito útil quando a ordem de execução de uma função não depende da outra.

Promise
    .all([pegaIdade(), pegaIdade(), pegaIdade()])
    .then(([idade1, idade2, idade3]) => {
        console.log("idade1: " + idade1);
        console.log("idade2: " + idade2);
        console.log("idade3: " + idade3);
    })
    .catch(error => console.error(error))

Promise também permite encadeamento do retorno, para quando você precisa tratar o dado antes de ir pra próxima etapa.

pegaIdade()
    .then((idade) => {
        if (idade < 18) {
            throw "Menores não permitidos"
        }

        // Esse return jogará uma nova resposta que será executada pelo próximo then
        return idade % 2 ? false : true;
    })
    .then(ePar => console.log(ePar))
    .catch(error => console.error(error))

Quando fazemos esse encadeamento de then é porque uma resposta depende de alguma chamada assíncrona. Mas ir encadeando um monte de then pode deixar o código meio confuso.

Aí entram o async/await marcando a função.

Dentro de uma função marcada com async podemos indicar que vamos esperar a Promise executar e retornar o seu valor ao invés de aguardar a Promise chamar nossa callback. Isso fazemos com o await. Desta forma quando temos várias chamadas a funções assíncronas e que uma chamada dependa da anterior fazemos um código mais linear ao invés de vários encadeamentos.

Mas quando fazemos desta forma o erro que a Promise dispara vira uma exceção. Por isso precisamos tratar com try...catch.

/**
 * @returns {Promise<boolean>}
 */
async function reserva() {
    try {
        const idade = await pegaIdade();
        
        if (idade < 18) {
            throw "Menores não permitidos";
        }

        // Imagina que aqui retorna um boolean
        const reservaEfetuada = await efetuaReserva();

        return reservaEfetuada
    } catch (error) {
        console.error(error);
        return false;
    }
}

Importante também ressaltar que quando marcamos uma função com async estamos transformando ela numa Promise.

Olá, tudo bem brunogasparetto? Cara, sensacional essa explicação mais detalhada da Promise que você deu, realmente vale a pena ler.