Async/Await returning Promise{<pending>}

Asked

Viewed 3,761 times

1

const url = 'http://files.cod3r.com.br/curso-js/funcionarios.json'
const axios = require('axios')    

const busca = () =>{
        return new Promise((resolve, reject) =>{
            try{
                const funcionarios = axios.get(url).then(resp => resp.data)
                resolve(funcionarios)
            }
            catch(e){
                reject(e)
            }
        })
    }

    async function getFuncionarios(){
        const funcionarios = await  busca()
        return funcionarios
    }

    const fcs = getFuncionarios()
    //fc deveria vir carregado com todos os funcionarios da url, mas só vem o Promise{<pending>}.Simplesmente não tem como ?
  • Just like you used await in await busca() you should also use in const fcs = await getFuncionarios(), but the place you call also has to be async. You can use then as well, but it is important that you understand how callbacks https://answall.com/q/45706/3635 work and understand how Promises: https://answall.com/q/119907/3635work

3 answers

1

First, let’s try to understand certain concepts.

How to create and use Promises?

In Javascript, when you will create a Promise using the default constructor, you pass a function that has two parameters:

  • resolve, which is the function to be called when the Promise was successfully completed, i.e., was "settled";

  • reject, which is the function to be called when the Promise found a problem and want to throw an error, that is, was "rejected".

Thus, using the original "API" of Promises, you would deal with them like this:

const myPromise = new Promise(function(resolve, reject) {
  // Tenta gerar um número aleatório de 1 a 20
  const randomNumber = Math.floor(Math.random() * 20 + 1);
  
  // Após 2 segundos...
  setTimeout(function() {
  
    // Se o número gerado for ímpar,
    // "resolve" (conclui) a Promise
    // passando o número gerado
    if (randomNumber % 2 == 1) {
      resolve(randomNumber);
    }
    
    // Se não for ímpar, rejeita a Promise
    // (gera um erro)
    else {
      reject(new Error('even number'));
    }
    
  }, 2000);
});


// Como não sabemos se a Promise vai
// ser concluída normalmente (resolvida),
// ou se ela vai gerar um erro (vai ser
// rejeitada), iremos esperar pelas duas
// coisas!

myPromise
  // Se ela concluir normalmente, mostramos
  // no console o número que ela nos forneceu 
  .then((number) => console.log(number))
  
  // Se ela gerar um erro, nós jogamos
  // esse erro no console também
  .catch((err) => console.error(err));

One thing to note is that instead of the chained call (the .then() followed by .catch()), you might get to see something like that, too:

myPromise.then(
  (number) => console.log(number),
  (err) => console.error(err),
);

That is, the passage of the function that handles the error, directly in the .then(), which is also valid.

The issue of the chain-up is due to the fact that the .then() also return a Promise. That way, you can even do it:

myPromise.then(function (number) {
  return `Number is: ${number}`;
}).then(
  (text) => console.log(text)
);

That would be the same as:

const secondPromise = myPromise.then(function (number) {
  return `Number is: ${number}`;
});

secondPromise.then((text) => console.log(text));

So, by acting out:

  • To create a Promise (using the standard builder), you must pass a function that takes as argument:

    • A function resolve, to be called when everything goes right;
    • And a function reject, to be called when a problem occurs.

    Remembering that, it is not necessary to be exactly these names, this is just a convention to make more understandable, more "didactic".

  • To wait for the result of a Promise, using the original API, you can simply use the method .then(), to establish the functions that will be called into resolve and reject, or you can use the .then() to establish the function of resolve and the .catch() to establish the function of reject, through the chain;

  • And chaining becomes possible, because the .then() also returns a Promise.


However, with the use of this API, you may face certain problems when creating features that perform various asynchronous operations. That is, they use several Promises. Let’s take an example:

function getFilteredData(filter) {
  return new Promise(function(resolve, reject) {
    // Eu faço uma requisição a uma suposta API externa de recursos
    const response = fetch('https://example.com/api/resource?filter=' + filter);
    
    // Como o código irá se basear inteiramente na resposta
    // da API, eu irei colocar praticamente todo o código da
    // da função em si, no `.then()` ou no `.catch()`
    response
      .then((data) => data.json())
      .then(function (json) {
        Promise.all(
          json.map((piece) =>
            new Promise(function (res, rej) {
              const response = fetch(`https://example.com/api/rating?id=${piece.id}`);
              response
                .then((data) => data.json())
                .then((rating) => res({ ...piece, rating }))
                .catch((e) => rej(e));
            })
          )
        )
        .then((newJson) => resolve(newJson))
        .catch((err) => reject(err));
      })
      .catch((error) => reject(error));
  });
}

const filteredData = getFilteredData('some-filter');

filteredData
  .then((data) => console.log(data))
  .catch((err) => console.error(err));

Do you see how many threads were made? And yet, the "traffic jam" that appeared in the code? Of course, certain things (maybe) could be optimized, but it was just a demonstration of how the PromiseIt can make things a little "complicated".

Thus, to facilitate the way you deal with Promises, were created the async and the await.

Use of async and await

You can see several posts here at Sopt we also talk about them. But basically, we can say that:

  • The async allows you to mark a function as a "asynchronous operations performer" and that therefore itself will become a asynchronous operation, which means it will generate a Promise. Also, mark a function with async is what allows the use of the command itself await.

  • And the command await is what allows you hold on that a Promise be completed, then follow with a certain code.

Let’s see them in practice, using them to recreate the code above:

// Marcamos com `async`, então o retorno
// de `getFilteredData` será uma `Promise`
async function getFilteredData(filter) {
  try {
    // Esperamos o resultado da `Promise` gerada por `fetch`
    const response = await fetch('https://example.com/api/resource?filter=' + filter);

    // Esperamos o resultado da `Promise` gerada pelo método `.json()`
    const json = await response.json();
    
    const data = [];
    
    for (const piece of json) {
      // Novamente, esperamos o resultado da `Promise` gerada por `fetch`
      const response = await fetch(`https://example.com/api/rating?id=${piece.id}`);

      // E o resultado da `Promise` gerada pelo método `.json()`
      const rating = await response.json();
      
      data.push({ ...piece, rating });
    }
    
    // Após "tanta espera", retornamos o resultado final
    return data;
  } catch (err) {
    // Se um erro foi capturado, iremos lançá-lo novamente
    throw err;
  }
}

const filteredData = getFilteredData('some-filter');

// Falaremos sobre isso mais adiante

filteredData
  .then((data) => console.log(data))
  .catch((err) => console.error(err));

You might think: wait, if we’re dealing with several Promises, why we only have a single block catch there?

The answer is: because in that case, when an error is generated, whatever operation generates it, the normal execution of the code will be stopped. Therefore, whatever point the error is generated, it will only be generated 1 time, because all other points that can generate error will no longer be executed. Let’s take a closer look:

  try {
    // Se gerar erro, o restante não executa
    const response = await fetch('https://example.com/api/resource?filter=' + filter);

    // Se gerar erro, o restante não executa
    const json = await response.json();
    
    const data = [];
    
    for (const piece of json) {
      // Se gerar erro, o restante não executa
      const response = await fetch(`https://example.com/api/rating?id=${piece.id}`);

      // Se gerar erro, o restante não executa
      const rating = await response.json();
      
      data.push({ ...piece, rating });
    }
    
    return data;
  }

Or perhaps, a functional example:

let fetchFailsCount = 0;

// A cada vez que `fetch` conseguir
// ser chamado e uma nova `Promise`,
// então, for criada, o contador será
// incrementado
function fetch() {
  return new Promise(function(_, reject) {
    fetchFailsCount++;
    setTimeout(() => {
      reject(`Fetch falhou ${fetchFailsCount} vez(es)`);
    }, 1000);
  });
}

(async function () {
  try {
    // Como o primeiro `fetch` gera
    // um erro, o segundo `fetch` não
    // será chamado
    const response1 = await fetch();
    const response2 = await fetch();
    
    console.log('Operações bem-sucedidas!');
  } catch (err) {
    console.log('Erro: ' + err);
  }
})();

Therefore, in this case, since only 1 error will occur during the entire execution of the function code, only 1 block is needed catch, thus capturing any of the errors that are generated by any of the operations. But if you want to deal with each mistake individually, treat it specifically, feel free.

However, with all this, we can make an analogy that the command await "replaces" the .then(), and the block try-catch "replaces" the .catch().


But then you can still think: and why, in the end, we do not use the command await also?

As far as is known, the use of the await out of asynchronous functions (those marked with async). That is to say:

// Por enquanto, ainda pode gerar um erro
const response = await fetch('http://example.com');

But if you really want to use await, you can involve the operation in an asynchronous function anonymous and at the same time call her that way:

(async function () {
  const filteredData = await getFilteredData('some-filter');
})();

It works as an alternative. But, have already proposed to bring this to the ES. We just don’t know if it will actually be implemented and if it will be soon.

Analyzing the code and trying to solve the problem

So let’s go to your code:

const url = 'http://files.cod3r.com.br/curso-js/funcionarios.json'
const axios = require('axios')    

const busca = () => {
  return new Promise((resolve, reject) => {
    try {
      const funcionarios = axios.get(url).then(resp => resp.data)
      resolve(funcionarios)
    }
    catch(e) {
      reject(e)
    }
  })
}

async function getFuncionarios() {
  const funcionarios = await busca()
  return funcionarios
}

const fcs = getFuncionarios()

In the function contained in busca, you "mixed up" a few things.

How you used the original API to wait for Promise of axios, it would no longer be right to use the try-catch to capture the error returned by it. Therefore, the try-catch would only work for real when you waited for Promise using await, which is when the true error would be cast.

Thus, if the axios return an error, you would actually capture the error of UnhandledPromiseRejection, that is to say, rejection of Promise not captured (free translation), which exactly corresponds to the problem of not having passed the function to be executed when the Promise is rejected (function to be executed in reject).

Furthermore, as we have seen previously, the .then() also returns a Promise. Therefore, that resolve is actually going through Promise of .then(), and not the value returned by it, to whom called the function busca. That is, the caller of the function busca would have to deal with two Promises: the Promise generated by function busca and the Promise returned by function busca.

// Gera uma Promise
const promise1 = busca();

// Retorna outra Promise
const promise2 = await promise1;

// Só então retorna o valor
const resultado = await promise2;

And that’s the cause of the problem. When you do await busca(), you are getting as value another Promise. And so, one would have to wait once again for the resolution of that Promise returned, to then get the desired value. And this is not something very "interesting" to do.


So if you want to use the original API, you can do so:

const url = 'http://files.cod3r.com.br/curso-js/funcionarios.json'
const axios = require('axios')    

const busca = () => {
  // Reutilizamos a `Promise` do `axios`,
  // através do funcionamento do .then()
  return axios.get(url)
      .then(resp => resp.data);
}

async function getFuncionarios(){
  try {
    const funcionarios = await busca();
  
    return funcionarios;
  } catch (err) {
    console.log(err);
    return [];
  }
}

getFuncionarios().then((fcs) {
  // faz alguma coisa com o resultado
});

Or, if you want to use the async/await, you can do so:

const url = 'http://files.cod3r.com.br/curso-js/funcionarios.json'
const axios = require('axios')    

const busca = async () => {
  try {
    const response = await axios.get(url);
    const funcionarios = response.data;

    return funcionarios;
  } catch(e) {
    throw e;
  }
}

async function getFuncionarios(){
  try {
    const funcionarios = await busca();

    return funcionarios;
  } catch (err) {
    console.log(err);
    return [];
  }
}

(async function() {
  const fcs = await getFuncionarios();
})();

Completion

Note that in both codes we use the block try-catch, in function getFuncionarios, to deal with the Promise function-generated busca. This is because, whether using the API, or using async/await, to Promise of function busca can be rejected. And as in getFuncionarios we are using the await, this rejection would anyway become an error to be released. Therefore, we need to deal with this error as well.

Another observation is that in the same way we deal with "Promisesimultaneous s" the first time, using the API, we could also do it with async/await. You can see that in that reply post, demonstrating the use of the method .map() together with the alternative builder Promise.all.

And yes, there are other "builders" for Promise, in addition to the default constructor. Some of them are:

  • Promise.resolve(value), which serves to create a Promise which immediately concludes (resolve), returning the value passed to the parameter value.
  • Promise.reject(reason), which serves to create a Promise which is immediately rejected with the reason passed to the parameter reason.
  • And the Promise.all(iterable), mentioned above, which serves to create a Promise that resolves when all other contained in iterable, are resolved, or is rejected when at least one of them is rejected.

0

On a project I was doing I checked that using fetch(), what was returned was a Promise {state: 'pending'}. To solve this I used await this way:

fetch(url, {method:'POST')
   .then(async(p) => {
   var data = await p.json()
   console.log(data)
   //retorna o resultado esperado...
}

I don’t know if it’s the right one, but it worked. If anyone has any objections, write below.

-1

axios.get(url) returns a Promise, you’re passing a Promise to another Promise, and then calls an asynchronous function, expecting the result to be synchronous.

I don’t think you understand how it works Promise right, there are these articles that explain how it works Promise

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise https://developers.google.com/web/fundamentals/primers/promises?hl=pt-br

Why getFuncionarios works in a synchronous way, the code should be:

const url = 'http://files.cod3r.com.br/curso-js/funcionarios.json'
const axios = require('axios')    

const busca = () =>{
    return new Promise((resolve, reject) =>{
        try{
            const funcionarios = axios.get(url).then(resp => resp.data)
            resolve(funcionarios)
        }
        catch(e){
            reject(e)
        }
    })
}

async function getFuncionarios(){
    const funcionarios = await  busca().then(resPromise => await resPromise)
    return funcionarios
}

const fcs = await getFuncionarios()

Or the code could be simplified to:

const url = 'http://files.cod3r.com.br/curso-js/funcionarios.json'
const axios = require('axios')    

function getFuncionarios(){
    return axios.get(url).then(resp => resp.data)
}

try {
    const fcs = await getFuncionarios()

} catch(e) {
    // Faz outra alguma coisa se der erro
}
  • I don’t think you understand either, pal, none of your codes even compile.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.