Upgrade to Pro — share decks privately, control downloads, hide ads and more …

あなたの await、OS まで届いてる? strace で覗く JavaScript の非同期

あなたの await、OS まで届いてる? strace で覗く JavaScript の非同期

Vue Fes Japan 2026(2026-10-24)のライトニングトークの資料です。

await fetch() を strace で覗き、JavaScript の非同期処理が OS に何を伝えているのかを確かめました。await 自体は OS に届かず、OS に届いていたのは fetch が返す Promise を解決するための処理(libuv が出すソケット操作と、イベントループの poll フェーズで応答を待つ epoll_pwait)でした。直列の await と Promise.all の違いや、Nuxt の useFetch でも同じ形になることも見ています。

検証一式(Docker で全実験を再現できます): https://github.com/HasutoSasaki/vuefes-2026-await-strace

Avatar for hasuto sasaki

hasuto sasaki

October 24, 2026

More Decks by hasuto sasaki

Other Decks in Programming

Transcript

  1. 非同期処理のおさらい async.mjs async function main() { console.log(1); await fetch('/a'); console.log(3);

    } main(); console.log(2); node async.mjs の実行結果 1 2 3 © 2026 Classmethod, Inc. 2
  2. 確かめ方 marks.mjs process.stdout.write('MARK A\n'); // ここで await する(fetch() / Promise.resolve())

    process.stdout.write('MARK B\n'); terminal strace -f -tt -T \ -e trace=socket,connect,epoll_pwait,write,read \ node marks.mjs fetch © 2026 Classmethod, Inc. 4
  3. 計測した環境 Node.js v22.23.3 / strace 6.1 Debian 12(Linux 6.8、aarch64) 手元の

    Mac の Docker(Colima)上で計測 ※ epoll_pwait は Linux の仕組み。macOS では kqueue、Windows では IOCP © 2026 Classmethod, Inc. 5
  4. await fetch() の場合 marks.mjs fetch process.stdout.write('MARK A\n'); const res =

    await fetch('/a'); await res.text(); process.stdout.write('MARK B\n'); © 2026 Classmethod, Inc. 7
  5. await fetch() の strace strace write(1, "MARK A...") = 7

    <0.000015> 通信部分 // TCP socket(AF_INET, SOCK_STREAM|..., IPPROTO_IP) = 21 <0.000027> connect(21, 127.0.0.1:3000) = -1 EINPROGRESS <0.000749> write(21, "GET /a HTTP/1.1...") = 170 <0.000036> // 応答待ち epoll_pwait(15, [{events=EPOLLIN, data={u32=21, u64=21}}], 1024, 482, NULL, 8) = 1 <0.013712> read(21, "HTTP/1.1 200 OK...") = 172 <0.000016> write(1, "MARK B...") = 7 <0.000015> © 2026 Classmethod, Inc. 8
  6. await 自体が OS に何か依頼している? marks.mjs resolve process.stdout.write('MARK A\n'); await Promise.resolve();

    process.stdout.write('MARK B\n'); strace write(1, "MARK A...") = 7 <0.000017> write(1, "MARK B...") = 7 <0.000019> © 2026 Classmethod, Inc. 9
  7. Node.js の中の層 Node.js ( が実⾏) JavaScript V8 undici fetch net

    の本体 Promise TCP ソケット の続き(マイクロタスク)は V8 が管理 の C++ 層(tcp_wrap) の net と libuv をつなぐ Node JS libuv イベントループ・システムコールを呼ぶ システムコール ( ) OS Linux © 2026 Classmethod, Inc. TCP モックサーバー 10
  8. I/O 待ちはどこで起きている? undici libuv のイベントループ timers net Node close pending

    check idle / prepare の C++ 層(tcp_wrap) libuv (Linux) OS © 2026 Classmethod, Inc. epoll_pwait() poll 11
  9. 2 パターンの書き方で比べる ここからは、モックサーバーが 300ms 待ってから応答します A 直列 const a =

    await fetch('/a'); const b = await fetch('/b'); B Promise.all const [a, b] = await Promise.all([ fetch('/a'), fetch('/b'), ]); © 2026 Classmethod, Inc. 13
  10. パターンA の strace // 1 A 直列 本目:TCP通信部分 socket(AF_INET, SOCK_STREAM|...,

    IPPROTO_IP) = 21 <0.000043> connect(21, 127.0.0.1:3000) = -1 EINPROGRESS <0.000086> write(21, "GET /a HTTP/1.1...") = 170 <0.000060> // 1 本目:応答待ち epoll_pwait(15, [...], 1024, 494, NULL, 8) = 1 <0.305597> read(21, "HTTP/1.1 200 OK...") = 172 <0.000071> // 2 本目:TCP通信部分 socket(AF_INET, SOCK_STREAM|..., IPPROTO_IP) = 22 <0.000080> connect(22, 127.0.0.1:3000) = -1 EINPROGRESS <0.000158> write(22, "GET /b HTTP/1.1...") = 170 <0.000090> // 2 本目:応答待ち epoll_pwait(15, [], 1024, 166, NULL, 8) = 0 <0.169480> epoll_pwait(15, [...], 1024, 494, NULL, 8) = 1 <0.124177> read(22, "HTTP/1.1 200 OK...") = 172 <0.000088> © 2026 Classmethod, Inc. 14
  11. パターンB の strace B Promise.all 通信部分(2 本) // TCP socket(AF_INET,

    SOCK_STREAM|..., IPPROTO_IP) = 21 <0.000040> connect(21, 127.0.0.1:3000) = -1 EINPROGRESS <0.000120> socket(AF_INET, SOCK_STREAM|..., IPPROTO_IP) = 22 <0.000030> connect(22, 127.0.0.1:3000) = -1 EINPROGRESS <0.000078> write(21, "GET /a HTTP/1.1...") = 170 <0.000038> write(22, "GET /b HTTP/1.1...") = 170 <0.000034> // 応答待ち epoll_pwait(15, [...], 1024, 493, NULL, 8) = 1 <0.303662> read(21, "HTTP/1.1 200 OK...") = 172 <0.000032> read(22, "HTTP/1.1 200 OK...") = 172 <0.000029> © 2026 Classmethod, Inc. 15
  12. A と B で I/O 待ちはどう違う? A 直列(約 640ms) JS

    fetch('/a') fetch('/b') socket / connect write a libuv () (poll) a の応答待ち socket / connect write b () epoll_pwait OS 300ms B Promise.all JS (poll) b の応答待ち epoll_pwait 300ms (約 330ms) fetch('/a') fetch('/b') libuv OS socket / connect write a b ( と ) epoll_pwait (poll) の応答待ち 300ms b の応答待ち 300ms a 2 本分の待ちが 1 回の I/O 待ちに重なる 時間 © 2026 Classmethod, Inc. 16
  13. Nuxt で await useFetch を 2 回並べる serial.vue const {

    data: a } = await useFetch('/a') const { data: b } = await useFetch('/b') strace(初回リクエスト 748ms) connect(23, 127.0.0.1:3000) = -1 EINPROGRESS <0.000091> write(23, "GET /a HTTP/1.1...") = 170 <0.000042> epoll_pwait(15, [...], 1024, 495, NULL, 8) = 1 <0.309949> connect(24, 127.0.0.1:3000) = -1 EINPROGRESS <0.000211> write(24, "GET /b HTTP/1.1...") = 170 <0.000117> epoll_pwait(15, [], 1024, 162, NULL, 8) = 0 <0.166521> epoll_pwait(15, [...], 1024, 498, NULL, 8) = 1 <0.136920> © 2026 Classmethod, Inc. 18
  14. Nuxt で Promise.all を使って束ねる parallel.vue const [{ data: a },

    { data: b }] = await Promise.all([ useFetch('/a'), useFetch('/b'), ]) strace(初回リクエスト 436ms) connect(23, 127.0.0.1:3000) = -1 EINPROGRESS <0.000103> connect(24, 127.0.0.1:3000) = -1 EINPROGRESS <0.000054> write(23, "GET /a HTTP/1.1...") = 170 <0.000041> write(24, "GET /b HTTP/1.1...") = 170 <0.000028> epoll_pwait(15, [...], 1024, 493, NULL, 8) = 1 <0.305665> © 2026 Classmethod, Inc. 19
  15. await fetch を strace で覗いた結果 await 自体は OS に届きません。 OS

    に届いていたのは、fetch が返す Promise を解決するための処理でした。 libuv が出すソケット操作と、poll フェーズで 応答を待つ epoll_pwait です © 2026 Classmethod, Inc. 21
  16. 記事とリポジトリ ログとコードはリポジトリで再現できます 検証一式(GitHub) https://github.com/HasutoSasaki/vuefes-2026-await-stra ce Docker で全実験を再現できます Node.js の非同期処理をシステムコールから覗く Node.js

    の fetch を strace で追う 複数 fetch の直列 vs Promise.all を strace で比べる JS の非同期処理は epoll で成り立っているのか (Node.js / Deno / Bun) https://dev.classmethod.jp/articles/nodejs-async-d eep-dive-with-syscalls/ https://dev.classmethod.jp/articles/nodejs-multiple -fetch-strace/ © 2026 Classmethod, Inc. https://dev.classmethod.jp/articles/nodejs-fetch-st race/ https://dev.classmethod.jp/articles/hasuto-js-asyn c-epoll-node-deno-bun/ 23