進階並行與非同步

延續(continuation,.then 組合)

你把一件外套留在乾洗店,告訴他們:「好了傳訊息給我,我再來燙好掛起來。」你並沒有站在櫃檯前乾等——你留下了一份「接下來該做什麼」的描述。延續正是如此:一段被登記、要在另一項計算完成之後才執行的工作,並把那項計算的結果當作輸入。最經典的寫法是 .then:future.then(next),意思是「當這個未來就緒時,把 next 套到它的值上執行」。

具體來說,你不在 future.get() 上阻塞、把執行緒卡住,而是掛上一個回呼。當生產者兌現了共享狀態,執行時期就拿著結果去呼叫你的延續——可能在某條工作者執行緒上,過程中沒有任何人傻等。因為 .then 本身會回傳一個新的未來(裝著回呼產生的東西),你可以串接:fetch(url).then(parse).then(render)。每一節都是一個延續,在前一個結果就緒時觸發,於是搭起一條相依步驟的管線,各階段之間從不阻塞執行緒。裸回呼的缺點——程式碼讀起來由內往外、深度巢狀的「回呼地獄」——正是 async/await 被發明來隱藏的東西;await 在底層,不過是把你函式的剩餘部分登記成一個延續的語法糖。

延續之所以重要,是因為它正是讓非同步結果在不阻塞下組合起來的方式。它是「做 X,然後拿到答案再做 Y」在一條保持忙碌的單一執行緒上的運作意義。兩個誠實的提醒:錯誤處理需要自己的路徑(通常是 .catch,或延續收到的是「結果或錯誤」),否則失敗會悄無聲息地消失;而你必須清楚延續會跑在哪一條執行緒上——以為它會回到原本的執行緒,而執行時期其實把它跑在池的工作者上,是資料競爭與 UI 執行緒臭蟲的常見來源。

JS:fetch(url).then(r => r.json()).then(data => render(data)).catch(err => log(err)); // 每個 .then 登記一個延續;各步驟之間都不阻塞主執行緒。

一串 .then 就是一條延續的管線,每一節在前一個結果就緒時觸發。

延續把「接下來執行什麼」與「由哪條執行緒執行」解耦。切勿假設 .then 的回呼會回到呼叫 .then 的那條執行緒——它可能跑在任何一個執行器工作者上,因此共享的可變狀態仍需同步。

又稱
.thencallback continuation回呼延續