require-atomic-updates

禁止由于使用 awaityield 而可能导致竞争条件的分配

在编写异步代码时,可能会产生微妙的竞争条件错误。考虑以下示例:

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 的初始值之和。如果在 getPageLength(pageNum) Promise 未决期间在单独的函数调用中更新了 totalLength 变量,则该更新将丢失,因为新值被覆盖而未被读取。

解决此问题的一种方法是确保在更新 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);
});

规则详情

Fgsu6tS5naQ0iqS4FiAK25QEK6egddp2Dt1zUDMT4Xx9K7zv7y+ayK+oLnHDyKk9VDy49LdE7ibNYucrv4tvuqXWavabfcov1nFDdOPxNyieT54M0wtmrs2oGm7LOyad

变量

Zzu48bEO8pzmsDX1nB32Fm2VEsRmY/rDWSk/qOYWNy2dCWi2/26yNA+9l/zF2Wd5jaOmowXtYXuDJJW+hltkQb3c85dSfBZ+g3SAPnA0oNapYwnmi1yWuJ2fio7MmiU5JJYRkItIBTs3pxsl1SsD3A==

  1. 变量被读取。
  2. yieldawait 暂停该功能。
  3. 函数恢复后,从第 1 步开始为变量赋值。

Loc3SdWEiTZQvAYu/U7EVKbyJs9F8vzWDsOjpUe5eOFJoTRk4V8XpPxB/TipDCKm0SApikoQbeV0herNyLiPCx2Kibj6+z9FOkqScpS4AVDU92BIMrxDk3oqLA+PznVE6wy46C7FXaIbLvf/LJTw5IibCovClv0I4Ov/W4utmsBCwlkRQmY7KXAaW/CmGmh1FOcXQNgpXQFxxyhdWmT7jeD9bWL9jiQsuwbtCI4NsQXrApHjDkT1ktjhEO94EwoRzuH+4xMozJFfVYsccDbiS1cC9Y99GSyWAR2BwdlPMf78jWUPHhRPYrqrbtRNyTzT6kaHhKbO+2wEtDE0GhlmqlHM3nc8Zcm1M/jNn8WrR7MT4WXr9Crdclo4PGEE6JkpJ2GObj7nA8lMRExYbhOehdvkN0h2aMv8JRLeH3q5Ae+/KZskDzCM1o+gtmcUAhiuPLAjzgydkIfvbTcP5Wxh8IO9bOoD8BPy4rsBxFipCFMGDrUTc05qhV9WhXxq+tp3

SxlwUOUGA9V1/uoc8ud3lek81IDSu79WflCB4CQfx7ukqQSfIieK6iwQZg3Kt2NFr9asyIVJsCuGABBzrd5qBVWQpBBi9XGm9gxVcI6VjYQzLs0wQkkuvXxqwCc8NsE3

    3kg5p0EboyCVrsekgs28qRBUv2oCshjIpoQGY7lNr6dqmyMSa0Ume45GxIbO/n6bchT3OB8LGw1+Gyhw7IlUM/sGRtp51i8n+0nrlGOqU12oZ6x6cZqInNc8hIS6pLDOaW+vrLwWt7hZAYn03t3sta56jT3MQKJYZW6mkp/yEWKfDK0KBaSPoWcjzP4DoPHJTC0Dl81Xrdr0P1PZuH40o4k3s3TCxJlw6Hkx2V6Wh/E=

53qbJC6xNcTpfWzDOxH3FbxQniXPcALMzc0838bz9bxwfaPpKG0ZV7pRGyxgA1aA

/* 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;
}

x8vS/D2xVZSiiHLs1PwUl6/TVEXV0uticb8dGcMm9rxiwxh8yRqnjwkHP0i+2Pam

/* 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;
}

属性

Zzu48bEO8pzmsDX1nB32Fm2VEsRmY/rDWSk/qOYWNy2dCWi2/26yNA+9l/zF2Wd5jaOmowXtYXuDJJW+hltkQb3c85dSfBZ+g3SAPnA0oNaricBe0AxpgRuIYHy/h+PyfHpJijLD8fJmF1+n9TyVOMJjT2aAEqjUy6Gg4+p6wAw=

  1. 读取变量或对象属性。
  2. yieldawait 暂停该功能。
  3. 函数恢复后,将值分配给属性。

Z1CW+AOq8nNRbS1aBT9d0mcZM15PoHIBz0t0VvIpCzpSqgzQDuCBr2Gtx3wcg+/lU4LOXWhsFbZPZtp/qgjJgLkGvXYIvG0hmmbRFpLCCnyP891Y/mY5Ll1KSvwijV1WTfvPEca/I79VfF088MNh7my61gcxrgm63K59KhxP89kVyc39JtH3gU36da9dBf3KWPSrP2ET8FX0VAjn3lCu0Q==

53qbJC6xNcTpfWzDOxH3FbxQniXPcALMzc0838bz9bxwfaPpKG0ZV7pRGyxgA1aA

/* eslint require-atomic-updates: error */

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

x8vS/D2xVZSiiHLs1PwUl6/TVEXV0uticb8dGcMm9rxiwxh8yRqnjwkHP0i+2Pam

/* eslint require-atomic-updates: error */

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

选项

GrNq2fetYiou0eUGLFslKXJzMat3EqRc4rAOvDlDY/Zx340ZWhiXWP+ysh/Dsnow

    SCzwxvbI23WDqfMVnQxdghz1Gu4EWyDMOCpiE66fHWXHUWJWjmcfjI5EOyEVOIClESwqD/4RYUEVS6M0Ft4GyG2uQLkdp79UFQhC6lod+akaIuonwAWPneW8vItOCqL4H9PD7wlmDz1ueU4BHNgJhUbywtbqBTQduBFMVtoaWGDlw9jQ1m7a3K/sxcJrBwfnBi//5JkV2LPW8Zj1BjFG3w==

allowProperties

5nEv4RCYCaBiSjFxJ4F/mM6JUW2lvyiJpQfy7dQT4O6N1AoF1k5fK+C94fIw4Ck6W+ZoAe2AxYi5WtFTaiXKS7ULBL/FJ5MAVcv6126gAtJtMIj4bO31Y2IKZJxJMUaN

/* eslint require-atomic-updates: ["error", { "allowProperties": true }] */

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

何时不使用

JtS2UXgPMciCMU6s3JF/dCmRYzxv5UI4OW9lwaKr6OdqhqdCkLUDCZsrmfvWPsXRfUtliIimMIB1w/f6/jsGIE6GyVC2NdSvmAL+m41WVYk=