優雅的使用CancellationToken取消一個任務
影片連結
什麼情況下會取消一個任務?
我們在日常生活中的專案中,會碰到長時間等待;使用者關閉瀏覽器的同時,讓背後需要花時間運行的工作停止,這樣做的好處是防止不必要的資源消耗,或是UI介面上有取消的按鈕可以讓使用者做互動的時候進行取消。
我相信大部分的開發人員並沒有實作這樣的Code(沒錯我也是🫠),但其實處理取消非同步或長時間同步作業是必須要被考慮進去的,想當年我剛成為開發人員的時候就有收到這樣的任務,使用者下載檔案的時候,會將文件轉換為PDF檔在UI上有一個進度條顯示轉換的進度。
後來收到了一個新的需求,要在前端的操作介面增加取消下載的按鈕,使用者可以隨時按下取消下載,這時候轉檔的任務要被取消,那時候很菜用了很不好的解決方法,就是在前端當使用者按下按鈕的時候,發送一個參數到後端,在方法裡面去偵測這個參數是否為true,為true的話會直接return,至今想想真的是超級鳥的🥲,轉換的動作沒有被立即停止,只是當檔案轉換完成後終止實際上已經完成轉換的任務了,現在回想起來應該使用CancellationToken來取消一個任務。
什麼是CancellationToken?
CancellationToken這個物件是可以一層一層往下傳遞至每個作業中,告知這個作業是否要被取消
我們可以使用LINQPad的Dump將其輸出看一下有什麼屬性

我們比較常使用的是IsCancellationRequested 他是一個 布林值 用來檢查取消請求是否已被觸發
在.net 的非同步方法的API中有大量地使用CancellationToken 我們以Microsoft.EntityFrameworkCore來舉例
常見的FirstOrDefaultAsync方法也有使用到喔
可以參考微軟官方的解說CancellationToken

動手實作一個小範例
我們來寫一個小範例來體驗一下怎麼使用CancellationToken 來取消一個任務
情境是當前端的使用者按下取消下載時,我們後端就會將檔案轉換PDF的任務被取消

我們先寫一個模擬檔案轉換的方法
這邊我們的方法很簡單,是模擬轉換檔案時顯示給使用者的進度條
接下來我們生成一個取消下載的Button

我們現在按下按鈕仍然沒有發生任何事情。
優雅的使用CancellationToken取消一個任務
首先我們要把轉換檔案的方法改成可以接受一個CancellationToken 參數的方法

同時在Main方法中生成一個CancellationTokenSource
CancellationTokenSource物件包含一個 CancellationToken 屬性
這個 CancellationToken 會被傳遞給非同步操作,並用於監控取消請求。
我們把CancellationTokenSource 的 Token 傳入檔案轉換的方法中

那什麼時候CancellationTokenSource的Token會被告知要取消呢?
就在我們按鈕被點下去的時候,所以我們要將Button 註冊一個Click事件

這邊使用Lambda表達式的匿名方法,來讓Click事件發生時CancellationTokenSource會觸發Cancel
接著回到我們的檔案轉換方法

增加了cts.IsCancellationRequested的判斷,也將這時候的CancellationToken列印出來

下載任務被中斷了!!
而且IsCancellationRequested變為true
這樣我們就做到了使用CancellationToken來取消一個任務了
如何變得更優雅?
實際上在專案中我們會更常使用到ThrowIfCancellationRequested()來丟出一個例外
這樣我們可以在呼叫方法時包上try catch
我們要捕捉的例外是OperationCanceledException
最後的Code是這樣,是不是又變得更優雅了呢~
