21
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JavaScript にも "マルチスレッド プログラミングの頻出問題" がある。

Posted at

元ネタ: eslint/eslint#10405

このルール提案は「アトミックではない変数の更新を警告したい」というものです。

アトミックではない変数の更新というのは、マルチスレッド プログラミングの最初の方に出てくるアレです。または、RDB のトランザクションの説明で出てくるアレです。

JavaScript のコードはシングルスレッドで動くのに、アトミックではない変数の更新なんてあるの? と思ったらありました。スレッドを自由に作成できる言語と違って、どこでも自由に別処理が割り込んでくるわけではありません。しかし、yieldawaitによって、式の途中で別処理が割り込んでくることがありえるのです。

See the Pen Threading problem by Toru Nagashima (@mysticatea) on CodePen.

肝はここです。

total += await getFileSize(file)

次のように動作します。

  1. 変数totalの値をメモリに読み込む。
  2. 処理await getFileSize(file)を実行して、結果をメモリに読み込む。
  3. 手順 1 と手順 2 の値を足し合わせた結果で変数totalの値を書き換える。

手順 2 がawait式を持っているため、ここに別処理が割り込むことができます。そして、その別処理の中で変数totalの値が変更された場合に意図しない結果になるのです。

この例では、reduce関数を使うなどして共有変数を持たないようにすべきですね。

function getTotalFileSize(files) {
    return Promise.all(files.map(getFileSize)).reduce((a, b) => a + b, 0)
}

別の例です。

const pattern = /\d{3}/g

// 文字列`test`の中にある特定パターンを列挙する関数。
function* iteratePattern(text) {
    let match = null

    pattern.lastIndex = 0
    while ((match = pattern.exec(text)) != null) {
        yield match
    }
}

この関数はyield式を持つため、yield matchの部分に別処理が割り込むことができます。すると、pattern.lastIndexがめちゃくちゃになってしまって、意図しない結果が生成される可能性があります。

21
8
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?