location.href 는 암묵적 await 가 아니다 — setter 는 동기, navigation 은 태스크

location.href에 값을 넣으면 그 줄에서 멈추고 이동할 것 같다 — 암묵적 await 처럼. 그럼 뒤의 코드는 안 돌까. 추측하지 말고 CDP로 깨 본다.

1. 실험을 건다

외부 도메인 대신 로컬 서버의 /a·/b로 이동시킨다(서버는 받은 요청을 기록한다). Runtime·Page·Network를 enable 하고, 마지막 식의 값까지 돌려받게 한 줄을 붙여 Runtime.evaluate로 실행한다.

location.href = '/a'
console.log('실행됨 1')
location.href = '/b'
console.log('실행됨 2')
// 'evaluate 끝까지 돌았다'

await였다면 첫 줄에서 멈춰 실행됨 1도 안 찍히고 /a로 가야 한다.

2. 이벤트 순서를 읽는다

CDP가 받은 순서 그대로다.1

ms 이벤트
514 Page.frameRequestedNavigation /a · scriptInitiated
516 Runtime.consoleAPICalled 실행됨 1
517 Page.frameRequestedNavigation /b
517 Runtime.consoleAPICalled 실행됨 2
517 Runtime.evaluate 응답 'evaluate 끝까지 돌았다'
518 Page.frameStartedNavigating /a
520 Network.requestWillBeSent /a
521 Network.loadingFailed net::ERR_ABORTED · canceled: true
523 Page.frameStartedNavigating · requestWillBeSent /b
528 Page.frameNavigated /b
  • setter는 동기로 끝나고 흐름을 안 끊는다. 이동 요청(frameRequestedNavigation)이 console 로그 사이사이에 찍히고, evaluate가 마지막 식의 값을 돌려준다 — 코드는 끝까지 돌았다.
  • 실제 이동은 그 뒤에 시작된다. frameStartedNavigating은 evaluate가 끝난 다음이다. 문서 언로드·페치는 태스크로 넘어가 지금 코드가 끝난 뒤에 돈다.
  • 마지막 값이 이기는 건 앞의 것이 취소되기 때문이다. /a가 시작됐다가 /b가 시작되며 ERR_ABORTED로 끊긴다.2 덮어쓴 게 아니다.
JS(콜 스택): href = '/a' → 실행됨 1 → href = '/b' → 실행됨 2 → evaluate 반환까지 끊기지 않고 돈다. href 를 넣을 때는 이동 요청만 등록된다. await 였다면 첫 href 에서 멈췄어야 한다. 브라우저 이동은 evaluate 가 끝난 517ms 뒤에 시작된다: /a 가 시작되어 요청까지 나갔다가 /b 가 시작되며 ERR_ABORTED 로 취소되고, /b 가 요청되어 528ms 에 frameNavigated 로 도착한다. 서버는 /a 와 /b 를 둘 다 받았다. 마지막 값이 이기는 건 덮어써서가 아니라 앞의 이동이 시작됐다가 취소되기 때문이다. 눈금은 비례가 아니다.

3. 덤 — 버려진 이동도 서버에는 갔다

서버 기록은 ["/a","/b"]다. /a는 requestWillBeSent까지 갔다가 클라이언트에서 끊겼을 뿐, 요청은 서버에 도착했다. 이동 전에 서버 부수효과가 있는 URL(로그아웃 링크 같은)이라면 “결국 /b로 갔으니 /a는 없던 일”이 아니다 — 572의 “취소는 클라이언트에서만 일어난다”와 같은 이야기다.

Footnotes

  1. Chrome 154 headless 에서 raw CDP 로 실측 (2026-10-01). ms 는 스크립트 시작 기준이고, evaluate 를 보낸 시각은 512ms 다. 로컬 Node 서버가 문서 요청 경로를 기록했다. ↩

  2. “Set the ongoing navigation for navigable to navigationId. This will have the effect of aborting other ongoing navigations of navigable, since at certain points during navigation changes to the ongoing navigation will cause further work to be abandoned.” — 그리고 문서 언로드·페치는 in parallel 로 넘어간다. (HTML Standard — navigate) ↩

#569