использование обещания с while l oop приводит к зависанию пользовательского интерфейса - PullRequest
1 голос
/ 21 июня 2020

Я пытаюсь заменить async / await на обещание (при сохранении логики / цели / результата кода), но изо всех сил пытаюсь понять проблему, возникающую при использовании обещания. Когда я пытаюсь выполнить новую версию кода с использованием обещаний, браузеры зависают и, кажется, выполняют бесконечное l oop. Не могли бы вы помочь мне понять, в чем проблема?

Вот рабочее состояние кода с использованием async / await

async fetchNewJokes() {
let newJokes = [];

while (newJokes.length < 10) {
  const {data} = await   axios.get('https://icanhazdadjoke.com/', {
    headers: {
      Accept: 'application/json',
    },
  });
  newJokes.push(data.joke);
 
}

this.setState({
    jokes: newJokes,
  });

}

Вот попытка замены asyn c await с обещанием

fetchNewJokes() {
let newJokes = [];

while (newJokes.length < 10) {
  axios.get('https://icanhazdadjoke.com/', {
    headers: {
      Accept: 'application/json',
    },
  }).then({data}=>{
  newJokes.push(data.joke);
 })
  
}
this.setState({
    jokes: newJokes,
  })
}

Ответы [ 2 ]

1 голос
/ 21 июня 2020

Прежде всего мы должны понять, что JS однопоточный. Код вашей программы обрабатывается только одним потоком. Он может запускать только один фрагмент кода (скажем, функцию) за раз. Помня об этом факте, давайте попробуем выяснить, что происходит в вашем коде.

async fetchNewJokes() {
    let newJokes = [];

    while (newJokes.length < 10) {
        /*1*/ const {data} = await axios.get('https://icanhazdadjoke.com/', {
          headers: {Accept: 'application/json'}});
        /*2*/newJokes.push(data.joke);
        this.setState({jokes: newJokes});
    }
}

JS выполняет эту функцию, пока не встретит await в строке \*1*\. Затем он выполняет axios.get() и возвращается к основному коду, не дожидаясь результата axios.get().

Далее он продолжает работать с другим кодом, делая другие полезные вещи, такие как рендеринг, обработка пользовательского ввода и так далее. Время от времени он проверяет завершено или нет axios.get().

Когда axios.get() завершено, он присваивает результат значению data и продолжает выполнение этой функции со строки \*2*\. На следующей итерации while l oop он снова встречает await и повторяет действия, описанные выше. И то же самое происходит для каждого шага while l oop.

Теперь давайте посмотрим на второй фрагмент кода.

fetchNewJokes() {
    let newJokes = [];

    while (newJokes.length < 10) {
        axios.get('https://icanhazdadjoke.com/', {
          headers: {Accept: 'application/json'}}).then(()=>{
            newJokes.push(data.joke);
            this.setState({jokes: newJokes});
        });
    }
}

JS выполняет эту функцию и выполняет axios.get(). Теперь для выполнения .then() JS необходимо время от времени проверять, завершено ли axios.get(). Но не может!

JS поток занят выполнением while l oop. Вы не даете ему времени заниматься чем-то другим. Он продолжает вызывать axios.get() снова и снова, не имея возможности проверить результат. Кроме того, поскольку JS занят выполнением l oop, у него нет времени на рендеринг. L oop потребляет все больше и больше ресурсов. Вот почему браузер зависает.

Ну и какое решение? @gdh уже дал вам один из них. Но если вам нужно внести меньше изменений в свой код, просто не полагайтесь на результат Promise в условии l oop.

fetchNewJokes() {
    for (let i=0; i<10; i++) {
        axios.get('https://icanhazdadjoke.com/', {
          headers: {Accept: 'application/json'}}).then(()=>{
            this.setState({jokes: this.state.jokes.concat(data.joke)});
        });
    }
}

В этом случае функция выполняет axios.get() десять раз и возвращает управление основному коду.

1 голос
/ 21 июня 2020

Обычно не устанавливайте состояние внутри, пока l oop, если вам не нужно. Ваш код async / await работал, потому что обновления состояния пакетируются, и состояние обновляется, когда выполнение охватывающей области завершено (то есть, пока l oop).

В вашем коде обещания состояние устанавливается в then блок. Таким образом, компонент повторно отображается после каждого вызова api и снова выглядит как fetchNewJokes и т. Д.

Поэтому используйте Promise.all, чтобы решить проблему.

Вот так

const fetchNewJokes = () => {
  let promiseArr = [];
  const counter = 0;

  while (counter < 10) {
    promiseArr.push(
      axios
        .get("https://icanhazdadjoke.com/", {
          headers: {
            Accept: "application/json",
          },
        })
        .then((data) => {
          //   newJokes.push(data.joke);
          return data.joke;
        })
    );
    counter++;
  }
  Promise.all(promiseArr).then((newJokes) => {
    this.setState({
      jokes: newJokes,
    });
  });
};

...