используя await inline для переменной во внешней области видимости - PullRequest
3 голосов
/ 23 мая 2019
(async function iife () {
  const numbers = [1, 2, 3, 4]
  let count = 0
  async function returnNumberAsync (number) {
    return new Promise(resolve => {
      setTimeout(() => resolve(number), 0)
    })
  }
  await Promise.all(numbers.map(async number => {
    count += await returnNumberAsync(number)
  }))
  console.log(count)
})()

Этот фрагмент регистрирует 4 на консоли, которая полностью за мной. Как только я назначу обещанное значение внутри map его локальной переменной…

const result = await returnNumberAsync(number)
count += result;

... регистрируется 10, как я и ожидал Что происходит, когда я count += await … ??

Ответы [ 2 ]

6 голосов
/ 23 мая 2019

Когда вы делаете count += await <expression>, начальное значение count, добавляемое к значению разрешения, сохраняется до того, как часть await будет разрешена. Так

count += await returnNumberAsync(number)

похоже на

count = count + await returnNumberAsync(number)

как видите:

(async function iife () {
  const numbers = [1, 2, 3, 4]
  let count = 0
  async function returnNumberAsync (number) {
    return new Promise(resolve => {
      setTimeout(() => resolve(number), 0)
    })
  }
  await Promise.all(numbers.map(async number => {
    count = count + await returnNumberAsync(number)
  }))
  console.log(count)
})()

В каждом из 4 замыканий count считается равным 0 до разрешения await с, поэтому только в последней микрозадаче await, где оно разрешается до

count += await returnNumberAsync(4)
// or
count = 0 + await returnNumberAsync(4)
// or
count = 4

делает 4 назначенным на count. Тот факт, что другие номера были присвоены count ранее, игнорируется. Они произошли, но назначение последней итерации - это все, что вы видите в текущем коде.

1 голос
/ 23 мая 2019

Вы всегда должны избегать изменения общего состояния внутри асинхронного кода.Если ваши операции не являются атомарными (count += await не является атомарным, поскольку он читает count, ожидает асинхронную задачу, а затем записывает в countcount может измениться за это время)), что может привести вас кбеда.

Ваш код можно легко изменить, чтобы он не использовал общее состояние:

 const count = (await Promise.all(numbers.map(returnNumberAsync))).reduce((a, b) => a + b, 0);
...