No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Curso de Next.js 2018

Curso de Next.js 2018

Roberto González

Roberto González

Performance de Get Initial Props

9/23
Recursos

Al utilizar await en cada fetch al traer los datos desde la API nos evitamos el callback hell, pero estamos haciendo que el usuario tenga que esperar cerca de un segundo y medio hasta que se completen secuencialmente las tres request.
En esta clase veremos cómo con Promise.all() solucionamos este problema de performance al paralelizar nuestras request.

Aportes 14

Preguntas 3

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Cada vez que tengamos la necesidad de realizar varias peticiones a una API, la mejor practica es usar Promise.all(), es una función que nos permite realizar varias peticiones en paralelo, nos resulta bastante útil para mejor la rapidez de carga.

Si quieren hacer tanto la request como la parte de pasar a JSON en la misma parte donde se hacen todas juntas, pueden usar .then()

Personalmente a mí me gusta más así para no declarar tantas variables ^^

static async getInitialProps({query}){
        let idChannel = query.id

        let [reqChannel, reqSeries, reqAudios] = await Promise.all([
            fetch(`https://api.audioboom.com/channels/${idChannel}`).then((response) => response.json()),
            fetch(`https://api.audioboom.com/channels/${idChannel}/child_channels`).then((response) => response.json()),
            fetch(`https://api.audioboom.com/channels/${idChannel}/audio_clips`).then((response) => response.json())
        ])

        let channel = reqChannel.body.channel;
        let series = reqSeries.body.channels;
        let audioClips = reqAudios.body.audio_clips;

        return { channel, audioClips, series }
    }```

Bueeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee e esto esta genial!

static async getInitialProps ({ query: { id }}) {
    const [reqChannel, reqAudio, reqSeries] = await Promise.all([
      fetch(`https://api.audioboom.com/channels/${id}`),
      fetch(`https://api.audioboom.com/channels/${id}/audio_clips`),
      fetch(`https://api.audioboom.com/channels/${id}/child_channels`)
    ])

    const [
      { body: { channel }},
      { body: { audio_clips: audioClips }},
      { body: { channels: series }}
    ] = await Promise.all([
      reqChannel.json(),
      reqAudio.json(),
      reqSeries.json()
    ])
    
    return {
      channel,
      audioClips,
      series
    }
  }

Usando aún mas la desestructuración y cachando errores

static async getInitialProps({ query: { id } }) {
    try {
      const [reqChannel, reqAudios, reqChilds] = await Promise.all([
        fetch(`https://api.audioboom.com/channels/${id}`),
        fetch(`https://api.audioboom.com/channels/${id}/audio_clips`),
        fetch(`https://api.audioboom.com/channels/${id}/child_channels`),
      ]);
      const { body: { channel } } = await reqChannel.json();
      const { body: { audio_clips } } = await reqAudios.json();
      const { body: { channels } } = await reqChilds.json();
      return { channel, audio_clips, channels };
    } catch ({ message }) {
      return (message);
    }
  }

No me gusto esta clase, se salta muchas cosas que en un principio son difíciles de entender

Porqué usamos let [req1, re2] en vez de const [req1, req2]?

Probé con const y me funciona muy bien. Por favor alguien digame que diferencia puede haber entre uno y otro.

Promise.all

Tener en cuenta que puede romperse:
Si alguna de las promesas pasadas en el argumento iterable falla, la promesa all es rechazada inmediatamente con el valor de la promesa que fué rechazada, descartando todas las demás promesas hayan sido o no cumplidas. - MDN

<h3>Para evitar que se termine el ciclo por “falla” implemente lo siguiente, no es lo mejor pero funciona, espero comentarios.</h3>

Gracias a esto si alguno falla continua la ejecución de las demás.

const [ requestChannel, requestClips, requestChilds ] = await Promise.all([
   request.get(`/channels/${idChannel}`, '').catch(error => error),
   request.get(`/channels/${idChannel}/audio_clips`, '').catch(error => error),
   request.get(`/channels/${idChannel}/child_channels`, '').catch(error => error)
]);

Utilicé otro cliente HTTP para utilizar las llamadas al API, pero utilicen la Fetch del curso.

Usando getServerSideProps y declarando funciones asíncronas, se puede tener un código más modulado:

Cada función asíncrona se encarga de obtener datos puntuales del canal.

async function getChannel(channelId) {
  const request = await fetch(`https://api.audioboom.com/channels/${channelId}`);
  const { body: { channel } } = await request.json();
  return channel;
}

async function getAudioclips(channelId) {
  const request = await fetch(`https://api.audioboom.com/channels/${channelId}/audio_clips`);
  const { body: { audio_clips } } = await request.json();
  return audio_clips;
}

async function getChildChannels(channelId) {
  const request = await fetch(`https://api.audioboom.com/channels/${channelId}/child_channels`);
  const { body: { channels } } = await request.json();
  return channels;
}

export async function getServerSideProps({ query }) {
  const [channel, audioClips, series] = await Promise.all([
    getChannel(query.id),
    getAudioclips(query.id),
    getChildChannels(query.id),
  ]);

  return {
    props: { channel, audioClips, series },
  };
}

Consulta como se deberia hacer el await si para el siguiente fecht necesita del anterior ya no servivio el all? ¿Qué metodo deberiamos utilizar?

Un poco mas corto

  static async getInitialProps({ query }) {
    let [reqChannel, reqAudioClips, reqSeries] = await Promise.all([
        fetch(`https://api.audioboom.com/channels/${query.id}`),
        fetch(`https://api.audioboom.com/channels/${query.id}/audio_clips`),
        fetch(`https://api.audioboom.com/channels/${query.id}/child_channels`)
    ]);
    let channel = (await reqChannel.json()).body.channel;
    let audioClips = (await reqAudioClips.json()).body.audio_clips;
    let series = (await reqSeries.json()).body.channels;
    return {channel, audioClips, series}
  }```

Aquí el código de ésta clase. Excelente tip del Promise.All()

export default class extends React.Component {
  static async getInitialProps({ query }) {
    let idChannel = query.id;

    let [reqChannel, reqSeries, reqAudios] = await Promise.all([
      //Mejora el performance de la App porque hace los request en paralelo
      fetch(`https://api.audioboom.com/channels/${idChannel}`),
      fetch(`https://api.audioboom.com/channels/${idChannel}/child_channels`),
      fetch(`https://api.audioboom.com/channels/${idChannel}/audio_clips`)
    ]);

    let dataChannel = await reqChannel.json();
    let channel = dataChannel.body.channel;

    let dataAudios = await reqAudios.json();
    let audioClips = dataAudios.body.audio_clips;

    let dataSeries = await reqSeries.json();
    let series = dataSeries.body.channels;

    return { channel, audioClips, series };
  }

  render() {
    const { channel, audioClips, series } = this.props;

    return (
      <div>
        <header>@viistorrr Podcasts</header>
        <h1>{channel.title}</h1>

        <h2>Ultimos Podcasts</h2>
        {audioClips.map(clip => (
          <div>{clip.title}</div>
        ))}

        <h2>Series</h2>
        {series.map(serie => (
          <div>{serie.title}</div>
        ))}

        <style jsx>{`
          header {
            color: #fff;
            background: #8756ca;
            padding: 15px;
            text-align: center;
          }
          .channels {
            display: grid;
            grid-gap: 15px;
            padding: 15px;
            grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
          }
          a.channel {
            display: block;
            margin-bottom: 0.5em;
            color: #333;
            text-decoration: none;
          }
          .channel img {
            border-radius: 3px;
            box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.15);
            width: 100%;
          }
          h1 {
            font-weight: 600;
            padding: 15px;
          }
          h2 {
            padding: 5px;
            font-size: 0.9em;
            font-weight: 600;
            margin: 0;
            text-align: center;
          }
        `}</style>

        <style jsx global>{`
          body {
            margin: 0;
            font-family: system-ui;
            background: white;
          }
        `}</style>
      </div>
    );
  }
}

Request paralelas a un api, mejora de performance

Si utilizan axios, no es necesario hacer la conversión a json:

Deben instalar primero axios e importarlo.

import axios from "axios";

//Capturamos la data de la api
 let data= await axios.get("https://api.audioboom.com/channels/recommended");

//La respuesta es un objeto con la propiedad data donde viene todo como un json

return data.data

Asi puede implementarse axios en el getInitialProps

channel.getInitialProps = async ({ query }) => {
    let idChannel = query.id;

    let URL1 = `https://api.audioboom.com/channels/${idChannel}`
    let URL2 = `https://api.audioboom.com/channels/${idChannel}/audio_clips`
    let URL3 = `https://api.audioboom.com/channels/${idChannel}/child_channels`

    let [ reqChannel, reqAudios, reqSeries ] = await Promise.all([ axios.get(URL1), axios.get(URL2), axios.get(URL3) ]);
    
    const dataChannel = await reqChannel.data;
    let channel = dataChannel.body.channel;

    const dataAudios = await reqAudios.data;
    let audioClips = dataAudios.body.audio_clips;

    const dataSeries = await reqSeries.data;
    let series = dataSeries.body.channels;

    let response = { channel, audioClips, series }
    return {...response}
}