Spec-Zone.ru › ESLint

require-atomic-updates

Запрещает присваивания, которые могут привести к состояниям гонки из-за использования await или yield

При написании асинхронного кода могут возникать трудноуловимые ошибки, связанные с состоянием гонки. Рассмотрим следующий пример:

let totalLength = 0;

async function addLengthOfSinglePage(pageNum) {
  totalLength += await getPageLength(pageNum);
}

Promise.all([addLengthOfSinglePage(1), addLengthOfSinglePage(2)]).then(() => {
  console.log('The combined length of both pages is', totalLength);
});

Кажется, что этот код складывает результаты вызовов getPageLength(1) и getPageLength(2), но на самом деле итоговым значением totalLength будет только длина одной из двух страниц. Ошибка заключается в выражении totalLength += await getPageLength(pageNum);. Сначала это выражение считывает исходное значение totalLength, затем вызывает getPageLength(pageNum) и ожидает выполнения этого Promise. Наконец, оно присваивает totalLength сумму await getPageLength(pageNum) и исходного значения totalLength. Если переменная totalLength обновится в отдельном вызове функции, пока Promise getPageLength(pageNum) находится в ожидании, это обновление будет потеряно, поскольку новое значение будет перезаписано без предварительного считывания.

Один из способов исправить эту проблему — убедиться, что totalLength считывается одновременно с обновлением, например так:

async function addLengthOfSinglePage(pageNum) {
  const lengthOfThisPage = await getPageLength(pageNum);

  totalLength += lengthOfThisPage;
}

Другой способ — вообще не использовать ссылку на изменяемую переменную:

Promise.all([getPageLength(1), getPageLength(2)]).then(pageLengths => {
  const totalLength = pageLengths.reduce((accumulator, length) => accumulator + length, 0);

  console.log('The combined length of both pages is', totalLength);
});

Подробное описание правила

Это правило предназначено для обнаружения присваиваний переменным или свойствам в случаях, когда присваиваемые значения могут основываться на устаревших данных.

Переменные

Это правило обнаруживает присваивание переменной, если в генераторе или асинхронной функции выявляется следующая последовательность выполнения:

  1. Считывается значение переменной.
  2. Выполнение функции приостанавливается на yield или await.
  3. После возобновления выполнения функции переменной из шага 1 присваивается значение.

О присваивании на шаге 3 сообщается, поскольку оно может быть обработано неверно: значение переменной из шага 1 могло измениться между шагами 2 и 3. В частности, если к переменной можно обратиться из других контекстов выполнения (например, если она не является локальной и поэтому другие функции могут её изменить), её значение могло измениться в другом месте, пока функция была приостановлена на шаге 2.

Обратите внимание: правило не сообщает о присваивании на шаге 3 в следующих случаях:

  • Если значение переменной считывается повторно между шагами 2 и 3.
  • Если к переменной нельзя обратиться, пока функция приостановлена (например, если это локальная переменная).

Примеры неправильного кода для этого правила:

Открыть в Playground
/* eslint require-atomic-updates: error */

let result;

async function foo() {
    result += await something;
}

async function bar() {
    result = result + await something;
}

async function baz() {
    result = result + doSomething(await somethingElse);
}

async function qux() {
    if (!result) {
        result = await initialize();
    }
}

function* generator() {
    result += yield;
}

Примеры правильного кода для этого правила:

Открыть в Playground
/* eslint require-atomic-updates: error */

let result;

async function foobar() {
    result = await something + result;
}

async function baz() {
    const tmp = doSomething(await somethingElse);
    result += tmp;
}

async function qux() {
    if (!result) {
        const tmp = await initialize();
        if (!result) {
            result = tmp;
        }
    }
}

async function quux() {
    let localVariable = 0;
    localVariable += await something;
}

function* generator() {
    result = (yield) + result;
}

Свойства

Это правило обнаруживает присваивание свойству через переменную, если в генераторе или асинхронной функции выявляется следующая последовательность выполнения:

  1. Считывается значение переменной или свойства объекта.
  2. Выполнение функции приостанавливается на yield или await.
  3. После возобновления выполнения функции свойству присваивается значение.

Эта логика похожа на логику для переменных, но строже, поскольку свойство на шаге 3 не обязательно должно совпадать со свойством на шаге 1. Предполагается, что последовательность выполнения зависит от состояния объекта в целом.

Пример неправильного кода для этого правила:

Открыть в Playground
/* eslint require-atomic-updates: error */

async function foo(obj) {
    if (!obj.done) {
        obj.something = await getSomething();
    }
}

Пример правильного кода для этого правила:

Открыть в Playground
/* eslint require-atomic-updates: error */

async function foo(obj) {
    if (!obj.done) {
        const tmp = await getSomething();
        if (!obj.done) {
            obj.something = tmp;
        }
    }
}

Параметры

Это правило принимает параметр-объект:

  • "allowProperties": если задано значение true, правило не сообщает о присваиваниях свойствам. Значение по умолчанию — false.

allowProperties

Пример правильного кода для этого правила с параметром { "allowProperties": true }:

Открыть в Playground
/* eslint require-atomic-updates: ["error", { "allowProperties": true }] */

async function foo(obj) {
    if (!obj.done) {
        obj.something = await getSomething();
    }
}

Когда не следует использовать это правило

Если вы не используете асинхронные функции или функции-генераторы, включать это правило не нужно.

Версия

Это правило появилось в ESLint v5.3.0.

Ресурсы

  • Исходный код правила
  • Исходный код тестов

© OpenJS Foundation and other contributors
Licensed under the MIT License.
https://eslint.org/docs/latest/rules/require-atomic-updates

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API