JavaScript 中使用fetch的AbortController和取消功能:如何在Go 實現

更新於 發佈於 閱讀時間約 13 分鐘
raw-image


有多種情況下我們需要從API取消請求:

  1. 如果我們正在建立一個支付系統,並且請求一直進行,但使用者希望立即取消這筆訂單。這就是為什麼開發者會建立這個功能的原因。
  2. 從電子商務購買商品。使用者意外關閉了頁面,無法立即返回上一頁。
  3. 訂閱取消。

......等等

這將對對這個系統沒有信心的使用者產生影響。因此,開發者需要認真對待這個問題。


我使用Vue和Go建立了一個簡單的前端系統和後端系統的示範。


後端部分

mkdir cancellation-demo && cd cancellation-demo

建立資料夾,稱為 `cancellation-demo`

go mod init callcellation-demo && \ 
touch main.go && \
go get github.com/gin-gonic/gin

從go module和main.go開始。導入gin套件。

以下是main.go的內容。建立一個簡單的Hello World。

package main

import (
"net/http"
"github.com/gin-gonic/gin"
)

func CancelHandler(ctx *gin.Context) {
ctx.String(http.StatusOK, "hello world")
}

func main() {
router := gin.Default()
router.GET("/", EchoHandler)
router.Run()
}

我們的目標是在收到請求後等待3秒,然後發送一個文字回應"Hello World"。如果使用者在3秒內取消了請求,我們將停止處理並打印結果,告知系統管理員該請求已被取消

以下範例

func CancelHandler(ctx *gin.Context) {
ctx.String(http.StatusOK, "hello world")
}

我們改寫成

func CancelHandler(ctx *gin.Context) {
responseResult := make(chan string)

go func() {
time.Sleep(3 * time.Second)
responseResult <- "hello world"
}()

for {
select {
case result := <-responseResult:
ctx.String(http.StatusOK, result)
return
case <-ctx.Request.Context().Done():
log.Println("abort triggerred.Cancellation happen")
ctx.AbortWithStatus(http.StatusNoContent)
return
}
}
}

讓我來解釋這部分:

我們需要建立一個goroutine和channel來傳送資料,並使用CancelHandler接收channel和ctx.Context().Done()來傳送chan類型。

這有助於我們檢查是否在請求被取消時收到中止訊號。這個方法將由中止訊號觸發。

因為我們需要建立一個在不同端口上監聽的前端專案,這導致了跨來源資源共享(CORS)的問題。因此,我們需要建立一個中間件(Middleware)來解決這個問題。

func CorsMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", "*")
ctx.Writer.Header().Set("Access-Control-allow-Credentials", "true")
ctx.Writer.Header().Set("Access-Control-allow-Method", "GET, POST, OPTIONS, PUT, PATCH, DELETE")
ctx.Writer.Header().Set("Access-Control-allow-Headers", "Authorization, Accept,Accept-Encoding, Content-Type,Content-Length, Cache-Control, Origin, X-CSRF-Token, X-Requested-With")
if ctx.Request.Method == http.MethodOptions {
ctx.AbortWithStatus(http.StatusNoContent)
 return
}
ctx.Next()
}
}

最後,我們的後端專案的部分就會看起來像這樣

package main

import (
"log"
"net/http"
"time"

"github.com/gin-gonic/gin"
)

func CorsMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", "*")
ctx.Writer.Header().Set("Access-Control-allow-Credentials", "true")
ctx.Writer.Header().Set("Access-Control-allow-Method", "GET, POST, OPTIONS, PUT, PATCH, DELETE")
ctx.Writer.Header().Set("Access-Control-allow-Headers", "Authorization, Accept,Accept-Encoding, Content-Type,Content-Length, Cache-Control, Origin, X-CSRF-Token, X-Requested-With")
if ctx.Request.Method == http.MethodOptions {
ctx.AbortWithStatus(http.StatusNoContent)
return
}
ctx.Next()
}
}

func CancelHandler(ctx *gin.Context) {
responseResult := make(chan string)

go func() {
time.Sleep(3 * time.Second)
responseResult <- "hello world"
}()

for {
select {
case result := <-responseResult:
ctx.String(http.StatusOK, result)
return
case <-ctx.Request.Context().Done():
log.Println("abort triggered")
ctx.AbortWithStatus(http.StatusNoContent)
return
}
}

}

func main() {
router := gin.Default()
router.Use(CorsMiddleware())
router.GET("/", CancelHandler)
router.Run()
}

現在換前端的部分

我們前端使用 vue 作為我們的前端框架

pnpm create vite --template vue-ts web && \
cd web && \
pnpm install

注意:對了,我本人個人是喜歡用 pnpm 作為我的 package manager,
不代表不能用 npm

然後我們將建立一個簡單的使用AbortController的fetch範例。這部分是向http://localhost:8080發送請求的部分。切記我們的後端專案在端口8080上監聽。

import { ref } from 'vue';
const abortReference = ref(new AbortController());

const getRequest = async () => {
  abortReference.value = new AbortController();
  try {
   const response = await fetch('http://localhost:8080', {
       method: 'GET',
      signal: abortReference.value.signal,
  })
   const mainContent = await response.text();
    console.log(mainContent);
  } catch{
   console.error('api error');
  }
}

取消功能的部分

const quitRequest = () => {
console.log('cancel request');
abortReference.value.abort();
}

現在我們把兩個方法取代原本專案的 app.vue

<script setup lang="ts">
import { ref } from 'vue';
const abortReference = ref(new AbortController());

const getRequest = async () => {
abortReference.value = new AbortController();
  try {
  const response = await fetch('http://localhost:8080', {
    method: 'GET',
     signal: abortReference.value.signal,
})
  const mainContent = await response.text();
  console.log(mainContent);
} catch{
  console.error('api error');
}
}

const quitRequest = () => {
console.log('cancel request');
abortReference.value.abort();
}

</script>

<template>
<button @click="getRequest">
click to request
</button>
<button @click="quitRequest">
Abort Request
</button>
</template>

這樣就大功告成了,

pnpm dev

然後打開瀏覽器我們會看到的結果

raw-image

如果有任何問題歡迎跟我討論,感謝你得閱讀

以下是我的 medium 帳號

還有我發表的英文版原文 ,如果對我的創作有興趣歡迎追蹤捧場

留言
avatar-img
留言分享你的想法!
avatar-img
Michael 的程式平台
3會員
5內容數
如果對網頁開發,區塊鏈開發,手機應用開發,桌面應用開發有興趣的可以來看看
2024/06/29
前言 從零開始構建一個 DateTimePicker 可能看起來令人畏懼,但試想一下你將獲得的靈活性和控制力。在這個系列中,我們將逐步揭開構建過程的神秘面紗,讓您能夠創建一個完全符合需求的自定義 DateTimePicker。 本文章,屬於付費系列的文章,這篇文章,我會希望讀者可以得到的
Thumbnail
2024/06/29
前言 從零開始構建一個 DateTimePicker 可能看起來令人畏懼,但試想一下你將獲得的靈活性和控制力。在這個系列中,我們將逐步揭開構建過程的神秘面紗,讓您能夠創建一個完全符合需求的自定義 DateTimePicker。 本文章,屬於付費系列的文章,這篇文章,我會希望讀者可以得到的
Thumbnail
2024/06/15
# 簡介 身為一位專注於 Vue.js 的前端開發者,這是我第一次嘗試構建 Flutter 網頁應用。讓我們開始吧! ## 第一次嘗試 ### 第一步:創建一個 Flutter 應用 首先,通過運行以下命令來創建一個新的 Flutter 項目: ```sh flutter
Thumbnail
2024/06/15
# 簡介 身為一位專注於 Vue.js 的前端開發者,這是我第一次嘗試構建 Flutter 網頁應用。讓我們開始吧! ## 第一次嘗試 ### 第一步:創建一個 Flutter 應用 首先,通過運行以下命令來創建一個新的 Flutter 項目: ```sh flutter
Thumbnail
2024/01/14
我看完影片只有一個想法,台灣目前還是用傳統方式開票還有利用人力的方式進行, 我認為這樣並不是很對,因為會增加人為疏失的可能性以外,還有可能造成做票或是小動作的嫌疑,我認為即便有人監票,那種很會『變魔術』的人,也會讓監票員造成困難 我身為一名軟體工程師,是要想 solution 的,
Thumbnail
2024/01/14
我看完影片只有一個想法,台灣目前還是用傳統方式開票還有利用人力的方式進行, 我認為這樣並不是很對,因為會增加人為疏失的可能性以外,還有可能造成做票或是小動作的嫌疑,我認為即便有人監票,那種很會『變魔術』的人,也會讓監票員造成困難 我身為一名軟體工程師,是要想 solution 的,
Thumbnail
看更多
你可能也想看
Thumbnail
TOMICA第一波推出吉伊卡哇聯名小車車的時候馬上就被搶購一空,一直很扼腕當時沒有趕緊入手。前陣子閒來無事逛蝦皮,突然發現幾家商場都又開始重新上架,價格也都回到正常水準,估計是官方又再補了一批貨,想都沒想就立刻下單! 同文也跟大家分享近期蝦皮購物紀錄、好用推薦、蝦皮分潤計畫的聯盟行銷!
Thumbnail
TOMICA第一波推出吉伊卡哇聯名小車車的時候馬上就被搶購一空,一直很扼腕當時沒有趕緊入手。前陣子閒來無事逛蝦皮,突然發現幾家商場都又開始重新上架,價格也都回到正常水準,估計是官方又再補了一批貨,想都沒想就立刻下單! 同文也跟大家分享近期蝦皮購物紀錄、好用推薦、蝦皮分潤計畫的聯盟行銷!
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
微前端是一種現代前端架構,旨在將大型前端應用拆分為獨立、可獨立部署的小模組。這與微服務在後端的策略相似。 本文將探討微前端的基本概念,以及如何在基於Gin的後端系統中實施它,從而實現真正的全棧模組化。
Thumbnail
微前端是一種現代前端架構,旨在將大型前端應用拆分為獨立、可獨立部署的小模組。這與微服務在後端的策略相似。 本文將探討微前端的基本概念,以及如何在基於Gin的後端系統中實施它,從而實現真正的全棧模組化。
Thumbnail
RESTful Web 服務是現代 Web 和移動應用的核心,它們提供了一個標準化的方式來互動和交換數據。使用Gin構建這些服務是一種流行選擇,還記得之前提過的 嗎?本文將介紹一些最佳實踐,幫助你創建高效和可維護的RESTful服務。
Thumbnail
RESTful Web 服務是現代 Web 和移動應用的核心,它們提供了一個標準化的方式來互動和交換數據。使用Gin構建這些服務是一種流行選擇,還記得之前提過的 嗎?本文將介紹一些最佳實踐,幫助你創建高效和可維護的RESTful服務。
Thumbnail
微服務架構是一種把應用程序劃分成一系列小的、獨立的服務,每個服務都運行在其自己的進程中,並與其他服務通過 API 或 RPC 通信。在這篇文章中,我們將使用 Gin 作為我們的 HTTP 框架,並使用 gRPC 來實現服務間的通信。
Thumbnail
微服務架構是一種把應用程序劃分成一系列小的、獨立的服務,每個服務都運行在其自己的進程中,並與其他服務通過 API 或 RPC 通信。在這篇文章中,我們將使用 Gin 作為我們的 HTTP 框架,並使用 gRPC 來實現服務間的通信。
Thumbnail
嗨,開發者們!在網頁應用的世界中,即時通訊是一個越來越重要的特點。WebSocket提供了一個高效的方式來支持這種通訊,並且能夠在客戶端和伺服器之間提供雙向的即時通訊。本篇文章將會帶你走進WebSocket的世界,並學習如何在Gin框架中實現它。
Thumbnail
嗨,開發者們!在網頁應用的世界中,即時通訊是一個越來越重要的特點。WebSocket提供了一個高效的方式來支持這種通訊,並且能夠在客戶端和伺服器之間提供雙向的即時通訊。本篇文章將會帶你走進WebSocket的世界,並學習如何在Gin框架中實現它。
Thumbnail
嗨,各位開發者!現代 Web 開發越來越傾向於前後端分離,使得前端框架和後端框架的協同工作變得非常重要。在這篇文章中,我們將專注於兩個極受歡迎的框架 —— Gin 和 Vue.js,探討如何將它們整合起來,為你的用戶提供流暢的Web 體驗。
Thumbnail
嗨,各位開發者!現代 Web 開發越來越傾向於前後端分離,使得前端框架和後端框架的協同工作變得非常重要。在這篇文章中,我們將專注於兩個極受歡迎的框架 —— Gin 和 Vue.js,探討如何將它們整合起來,為你的用戶提供流暢的Web 體驗。
Thumbnail
大家好!隨著微服務和前後端分離的架構日益普及,RESTful API 已經成為 Web 開發的核心。透過 Gin,我們可以輕鬆地設計和實現高效能的 API。今天,我們就來探討如何在 Gin 中設計優雅且實用的RESTful API
Thumbnail
大家好!隨著微服務和前後端分離的架構日益普及,RESTful API 已經成為 Web 開發的核心。透過 Gin,我們可以輕鬆地設計和實現高效能的 API。今天,我們就來探討如何在 Gin 中設計優雅且實用的RESTful API
Thumbnail
Gin框架深入解析:路由和處理器 Hey 朋友們!上回我們介紹了 Gin 框架的基礎知識,今天我們要更深入一些,看看 Gin 如何處理路由和相對應的處理器吧! Gin 的路由機制非常靈活和高效,可以快速地配置不同的路由方式。再配合上處理器函數,讓我們可以有針對性地處理不同路由的請求。探索完今
Thumbnail
Gin框架深入解析:路由和處理器 Hey 朋友們!上回我們介紹了 Gin 框架的基礎知識,今天我們要更深入一些,看看 Gin 如何處理路由和相對應的處理器吧! Gin 的路由機制非常靈活和高效,可以快速地配置不同的路由方式。再配合上處理器函數,讓我們可以有針對性地處理不同路由的請求。探索完今
Thumbnail
嗨各位!歡迎來到這個系列,今天我們要探索的是 Gin 框架!如果你對於開發高效且輕量級的 Web 應用感興趣,那你絕對不能錯過這篇!
Thumbnail
嗨各位!歡迎來到這個系列,今天我們要探索的是 Gin 框架!如果你對於開發高效且輕量級的 Web 應用感興趣,那你絕對不能錯過這篇!
Thumbnail
👨‍💻簡介 當我們在寫程式時,有時候會需要在程式結束時關閉某些資源,而defer這個關鍵字,可以讓你輕鬆的實現,下面來簡單介紹一下defer以及常用的範例。,它為程式設計師提供了一種簡單而強大的工具,用於管理資源和確保程式的正確執行。
Thumbnail
👨‍💻簡介 當我們在寫程式時,有時候會需要在程式結束時關閉某些資源,而defer這個關鍵字,可以讓你輕鬆的實現,下面來簡單介紹一下defer以及常用的範例。,它為程式設計師提供了一種簡單而強大的工具,用於管理資源和確保程式的正確執行。
Thumbnail
有多種情況下我們需要從API取消請求: 如果我們正在建立一個支付系統,並且請求一直進行,但使用者希望立即取消這筆訂單。這就是為什麼開發者會建立這個功能的原因。 從電子商務購買商品。使用者意外關閉了頁面,無法立即返回上一頁。 訂閱取消。 ......等等 這將對對這個系統沒有信心的使用者產生影響。因
Thumbnail
有多種情況下我們需要從API取消請求: 如果我們正在建立一個支付系統,並且請求一直進行,但使用者希望立即取消這筆訂單。這就是為什麼開發者會建立這個功能的原因。 從電子商務購買商品。使用者意外關閉了頁面,無法立即返回上一頁。 訂閱取消。 ......等等 這將對對這個系統沒有信心的使用者產生影響。因
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News