【问题标题】:Unanticipated Await/Async Behavior意外的等待/异步行为
【发布时间】:2018-02-23 12:43:01
【问题描述】:

我正在尝试通过等待/异步按顺序/同步顺序执行以下逻辑。

async loadData() {


    console.log("STEP 1");
    let ratesResponse = await getRates();
    let accounts = realm.objects("Account");

    accounts.map(async account => {
      let accountResponse = await getAccountBalance(account.accountid);
      console.log("STEP 2");
      realm.write(() => {
        account.cad_balance = accountResponse.balances[0].value;
        account.usd_balance = (
          parseFloat(accountResponse.balances[0].value) *
          parseFloat(ratesResponse.last)
        ).toString();
      });
    });
    console.log("STEP 3");
    this.setState({
      data: realm.objects("Account"),
      last: ratesResponse.last,
      high: ratesResponse.high,
      low: ratesResponse.low,
      refreshing: false
    });

}

但是,应用功能不起作用,并且日志确认第 3 步发生在第 2 步之前:

2018-02-22 22:53:10.922 [info][tid:com.facebook.react.JavaScript] STEP 1
2018-02-22 22:53:10.921822-0500[13978:3180532] STEP 1
2018-02-22 22:53:11.211 [info][tid:com.facebook.react.JavaScript] STEP 3
2018-02-22 22:53:11.210787-0500[13978:3180532] STEP 3
2018-02-22 22:53:11.233 [info][tid:com.facebook.react.JavaScript] STEP 2
2018-02-22 22:53:11.233529-0500[13978:3180532] STEP 2

我错过了什么?

【问题讨论】:

  • 它按您指定的方式工作。 .map() 中的异步回调返回一个承诺。您必须使用.then()console.log('Step 3');
  • Arman Charan 所说的也很有效。可能更接近您的设计目标。我看不见上面的async() waaaay。 :-)
  • 真的吗? accounts.map 返回一个可以等待的 Promise?
  • 你需要await(Promise.all(accounts.map( .... - 而且,accounts.map 函数实际上需要返回一些东西!! ...最好是承诺等待!!
  • @JaromandaX 从技术上讲,如果map() 中最后一个await 之后的所有内容都是同步的(我认为这里不是这种情况,但仍然如此),那么它实际上不需要返回自函数为 async 以来的任何内容。

标签: javascript react-native async-await


【解决方案1】:

第一件事 - 你需要等待 accounts.map(async account => {...}) 返回的一系列承诺

这可以使用 await Promise.all 来实现...即 Promise.all(accounts.map(async account => {...}))

其次,如果 realm.write 是异步,您需要等待一个 Promise,该 Promise 在该函数完成时解析

await new Promise(resolve => realm.write(resolve));

所以,把它们放在一起

async function loadData() {
    console.log('STEP 1');
    let ratesResponse = await getRates();
    let accounts = realm.objects('Account');
    await Promise.all(accounts.map(async account => {
        let accountResponse = await getAccountBalance(account.accountid);
        console.log('STEP 2');
        await new Promise(resolve => realm.write(resolve));
        account.cad_balance = accountResponse.balances[0].value;
        account.usd_balance = (parseFloat(accountResponse.balances[0].value) * parseFloat(ratesResponse.last)).toString();
    }));
    console.log('STEP 3');
    this.setState({
        data: realm.objects('Account'),
        last: ratesResponse.last,
        high: ratesResponse.high,
        low: ratesResponse.low,
        refreshing: false
    });
}

【讨论】:

  • return await -> return编辑 或者,是的。 await 也可以。
  • 天啊!!搞砸了:p
  • 愚蠢的 firefox 暂存器在使用漂亮的打印时会搞砸 async/await/let 等!!!
  • @Bergi - 有趣(而且非常好)的编辑!我只是假设a)realm.write是异步的,b)它以某种方式影响accountResponse等的值(如果确实如此,那肯定是一个不起眼的函数!!)
  • @JaromandaX 是的,我不知道realm.write,我希望它不会将回调用作事务,以便将这些语句带到外面会破坏它们。
【解决方案2】:

Array.prototype.map 不会在提供的函数上等待,因此在提供的函数中对 Array.prototype.map 的等待不会导致您期望的同步行为。

你有两个选择

  1. 使用在块中考虑await 的原生for in 运算符,例如

    异步加载数据() {

    console.log("STEP 1");
    let ratesResponse = await getRates();
    let accounts = realm.objects("Account");
    
    for (let account in accounts) {
      let accountResponse = await getAccountBalance(account.accountid);
      console.log("STEP 2");
      realm.write(() => {
        account.cad_balance = accountResponse.balances[0].value;
        account.usd_balance = (
          parseFloat(accountResponse.balances[0].value) *
          parseFloat(ratesResponse.last)
        ).toString();
      });
    }
    console.log("STEP 3");
    this.setState({
      data: realm.objects("Account"),
      last: ratesResponse.last,
      high: ratesResponse.high,
      low: ratesResponse.low,
      refreshing: false
    });
    

    }

  2. 或者在异步函数返回的承诺列表上等待

    异步加载数据() {

    console.log("STEP 1");
    let ratesResponse = await getRates();
    let accounts = realm.objects("Account");
    
    await Promise.all(accounts.map(async (account) => {
      let accountResponse = await getAccountBalance(account.accountid);
      console.log("STEP 2");
      realm.write(() => {
        account.cad_balance = accountResponse.balances[0].value;
        account.usd_balance = (
          parseFloat(accountResponse.balances[0].value) *
          parseFloat(ratesResponse.last)
        ).toString();
      });
    });
    console.log("STEP 3");
    this.setState({
      data: realm.objects("Account"),
      last: ratesResponse.last,
      high: ratesResponse.high,
      low: ratesResponse.low,
      refreshing: false
    });
    

    }

但是这两者的运作方式存在细微差别,第一个将逐个处理每个帐户,而第二个将并行处理所有帐户,但要等待并行执行完成才能继续。

【讨论】:

  • 感谢@user2182269,我使用了第二个选项,效果很好
【解决方案3】:

accounts.map(async() => ({})) 返回一个 promise 数组。

然后,您需要将该数组传递给 Promise.all()await

一旦该数组中的所有这些承诺都解决了,它将执行接下来的操作,即您的console.log('STEP 3')

async function loadData() {
    console.log('STEP 1')
    const ratesResponse = await getRates()
    const accounts = realm.objects('Account')

    const promises = accounts.map(async (account) => {
        const accountResponse = await getAccountBalance(account.accountid)
        console.log('STEP 2')
        realm.write(() => {
            account.cad_balance = accountResponse.balances[0].value
            account.usd_balance = (
                parseFloat(accountResponse.balances[0].value) *
                parseFloat(ratesResponse.last)
            ).toString()
        })
    })

    await Promise.all(promises)

    console.log('STEP 3')
    this.setState({
        data: realm.objects('Account'),
        last: ratesResponse.last,
        high: ratesResponse.high,
        low: ratesResponse.low,
        refreshing: false,
    })
}

【讨论】:

  • 两件事。一,我会确认realm.write() 是同步的。我非常怀疑它是。如果没有,您将需要返回一个 Promise,它等待来自 accounts.map() 的完成。二,由于父函数是async,你应该利用它,将Promise.all(...).then(...)转换为await Promise.all(...); ...
  • @PatrickRoberts 好点。我不知道realm.write() 做了什么。我认为解决方案的想法就在这里,OP应该能够应用它。
猜你喜欢
  • 2013-09-29
  • 1970-01-01
  • 2021-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-16
相关资源
最近更新 更多