【问题标题】:usage of promise with while loop results in freezing ui将 promise 与 while 循环一起使用会导致 ui 冻结
【发布时间】:2020-10-11 09:44:36
【问题描述】:

我正在尝试用 promise 替换 async/await(同时保留代码的逻辑/目的/结果),但很难理解使用 promise 导致的问题。当我尝试使用 Promise 执行新版本的代码时,浏览器冻结并且似乎正在执行无限循环。你能帮我理解问题是什么吗?

这里是使用 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,
  });

}

这是尝试用 promise 替换 async 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,
  })
}

【问题讨论】:

  • “你能帮我理解问题是什么吗?” - 试着理解 await 做了什么
  • "我正在尝试用 promise 替换 async/await" - async/await 基于 promise。您似乎正在尝试将 await 替换为 .then()
  • @Bergi 您能否详细说明您的评论?

标签: javascript reactjs while-loop async-await es6-promise


【解决方案1】:

一般情况下,除非需要,否则不要在 while 循环内设置状态。您的 async/await 代码有效,因为状态更新是批处理的,并且在封闭范围执行完成时更新状态(即 while 循环)。

在您的承诺代码中,状态设置为 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,
    });
  });
};

【讨论】:

    【解决方案2】:

    首先我们要明白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 执行这个函数,直到它在\*1*\ 线上遇到await。然后它执行axios.get()并返回主代码而不等待axios.get()的结果。

    它还继续与其他代码一起做其他有用的事情,比如渲染、处理用户输入等等。 它有时会检查axios.get() 是否完成。

    axios.get() 完成时,它会将结果分配给data 值,并从\*2*\ 行继续执行此功能。在while 循环的下一次迭代中,它再次遇到await 并重复上述操作。 while 循环的每一步都会发生同样的情况。

    现在让我们看看第二个代码 sn-p。

    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 循环。你不给它时间做其他事情。它继续一次又一次地调用axios.get(),没有任何可能检查结果。此外,由于 JS 忙于循环,它没有时间进行渲染。循环消耗越来越多的资源。这就是浏览器冻结的原因。

    那么有什么解决办法呢? @gdh 已经给了你其中之一。但是,如果您需要对代码进行较少的更改,请不要在循环条件下依赖 Promise 的结果。

    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() 并将控制权返回给主代码。

    【讨论】:

    • спасибо за развернутый ответ。 ПолучаетсяAWAITпозволяетвысвободитьсяизцикланакороктийпериодиприступитьквыполнениюдальнейшегокода,втовремякак。然后()сдерживаетвыполнениедальнейшегокода?跨度>
    • @esentai да,等待 высвобождает из цикла и позволяет выполнить дальнейший код。 .then() сработает только тогда, когда JS проверит и убедится, что вызов axios.get() выполнен。 Но такая проверка тоже требует времени。 Ацикл не прекращается и времени на проверку нет。 Выполнение кода сдерживает не then(), а бесконечный цикл。 Абесконечный он, потому что инкремент находится в then()。 Замкнутый круг.
    猜你喜欢
    • 2015-06-21
    • 2014-04-11
    • 2013-07-06
    • 2016-04-08
    • 2017-01-22
    • 2012-08-02
    • 1970-01-01
    • 2017-10-09
    • 2014-01-09
    相关资源
    最近更新 更多