意外的Await / Async行为

时间:2018-02-23 04:00:00

标签: javascript react-native async-await

我正在尝试通过await / async以顺序/同步顺序执行以下逻辑。

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
    });

}

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

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

我错过了什么?

3 个答案:

答案 0 :(得分:2)

首先 - 您需要等待accounts.map(async account => {...})

返回的承诺数组

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

其次,如果realm.write是异步,则需要等待在该函数完成时解析的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
    });
}

答案 1 :(得分:1)

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

您有两个选择

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

    async loadData(){

    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. 或等待async函数返回的承诺列表,如

    async 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");
      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 :(得分:0)

accounts.map(async() => ({}))返回一系列承诺。

然后,您需要将该数组传递给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,
    })
}