npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

polling-fetch

v1.0.5

Published

A TypeScript library that extends the native Fetch API with polling capabilities

Downloads

408

Readme

PollingFetch

English | 日本語

想讓原生的 Fetch API 能夠更好地處理長時間運行的任務嗎?來認識 PollingFetch 吧!

這個輕量但強大的函式庫為您喜愛的 Fetch API 帶來任務管理的超能力,讓那些耗時的伺服器操作變得輕鬆自如。不再需要複雜的狀態管理 - 就是這麼簡潔優雅!

為什麼選擇 PollingFetch?

  • 以任務為中心的設計:專為實際的伺服器操作而設計
  • 原生 Fetch 體驗:用起來就像您熟悉的 Fetch API
  • 類型安全:完整的 TypeScript 支援,避免討厭的錯誤
  • 實戰考驗:透過全面的測試覆蓋確保可靠性
  • 零依賴:輕量的程式碼,巨大的影響力

安裝

npm install polling-fetch

快速開始

import { PollingFetch } from 'polling-fetch';

// 啟動一個長時間運行的任務
const response = await PollingFetch('/api/tasks/process-data', {
  method: 'POST',
  body: JSON.stringify({ data: 'process this!' }),
  polling: {
    interval: 2000,  // 每2秒檢查一次
    onInitRespond: async (context) => {
      // 處理初始回應
      const { status } = context.initResponse;
      if (status === 400) {
        throw new Error('無效的請求');
      }
      if (status === 200) {
        return context.initResponse; // 立即完成
      }
      return undefined; // 繼續輪詢
    },
    onPolling: async (context) => {
      // 從初始回應中獲取 taskId
      const { taskId } = await context.initResponse.clone().json();
      
      // 檢查進度
      const statusResponse = await fetch(`/api/tasks/${taskId}/status`);
      const status = await statusResponse.json();
      
      if (status.completed) {
        // 任務完成!獲取最終結果
        const resultResponse = await fetch(`/api/tasks/${taskId}/result`);
        return resultResponse;
      }
      
      // 尚未完成,繼續輪詢
      return undefined;
    }
  }
});

const result = await response.json();

實際應用範例

帶進度的任務處理

const response = await PollingFetch('/api/tasks/analyze', {
  method: 'POST',
  body: JSON.stringify({ data: 'analyze this!' }),
  polling: {
    interval: 1000,
    // 在發送前添加認證
    onRequest: async (requestInput) => ({
      ...requestInput,
      headers: {
        ...requestInput.headers,
        'Authorization': `Bearer ${await getToken()}`
      }
    }),

    // 處理初始回應
    onInitRespond: async (context) => {
      const { status } = context.initResponse;
      if (!context.initResponse.ok) {
        throw new Error(`啟動任務失敗:${status}`);
      }
      // 在上下文中儲存任務資訊以供後續使用
      const { taskId } = await context.initResponse.clone().json();
      context.taskId = taskId;
      return undefined; // 繼續輪詢
    },

    // 監控進度
    onPolling: async (context) => {
      // 使用上下文中的 taskId
      const statusResponse = await fetch(`/api/tasks/${context.taskId}/status`);
      const status = await statusResponse.json();
      
      // 保持使用者了解進度
      if (status.progress) {
        updateProgressBar(status.progress);
      }
      
      switch (status.state) {
        case 'completed':
          // 成功!獲取結果
          const resultResponse = await fetch(`/api/tasks/${context.taskId}/result`);
          return resultResponse;
          
        case 'failed':
          throw new Error(status.error);
          
        case 'processing':
        case 'pending':
          return undefined; // 繼續監控
          
        default:
          throw new Error(`意外的狀態:${status.state}`);
      }
    }
  }
});

const result = await response.json();

優雅的任務取消

const controller = new AbortController();

try {
  const response = await PollingFetch('/api/tasks/heavy-computation', {
    signal: controller.signal,
    polling: {
      onPolling: async (context) => {
        const { taskId } = await context.initResponse.clone().json();
        
        const statusResponse = await fetch(`/api/tasks/${taskId}/status`);
        const status = await statusResponse.json();
        
        if (status.completed) {
          const resultResponse = await fetch(`/api/tasks/${taskId}/result`);
          return resultResponse;
        }
      },
      onAbort: async (context) => {
        // 在伺服器端清理我們的任務
        const { taskId } = await context.initResponse.clone().json();
        await fetch(`/api/tasks/${taskId}/cancel`, {
          method: 'POST'
        });
      }
    }
  });
} catch (error) {
  if (error instanceof Error && error.name === 'AbortError') {
    console.log('任務已取消');
  }
}

// 需要停止?沒問題!
controller.abort();

創建您的完美設定

const taskFetch = PollingFetch.create({
  interval: 2000,
  onPolling: async (context) => {
    const { taskId } = await context.initResponse.clone().json();
    const statusResponse = await fetch(`/api/tasks/${taskId}/status`);
    const status = await statusResponse.json();
    
    if (status.completed) {
      const resultResponse = await fetch(`/api/tasks/${taskId}/result`);
      return resultResponse;
    }
  }
});

API 參考

PollingConfig

輪詢行為的配置對象:

interface PollingConfig {
  // 輪詢間隔(毫秒)(預設:1000)
  interval?: number;
  
  // 在初始請求前調用
  onRequest?: (fetchInit: RequestInput) => Promise<RequestInput> | RequestInput;
  
  // 在初始回應後調用
  onInitRespond?: (context: IContext) => Promise<Response | any | undefined> | Response | any | undefined;
  
  // 在每次輪詢時調用
  onPolling?: (context: IContext) => Promise<Response | any | undefined> | Response | any | undefined;
  
  // 在請求被中止時調用
  onAbort?: (context: IContext) => Promise<void> | void;
}

Context 對象

傳遞給輪詢回調的上下文對象:

interface IContext {
  // 初始請求輸入
  requestInput: RequestInput;
  
  // 初始回應
  initResponse: Response;
  
  // 最新的輪詢回應
  pollingResponse?: Response;
  
  // 輪詢嘗試次數
  retryCount: number;
  
  // 輪詢開始時間戳
  startTime: number;
  
  // 當前輪詢配置
  config: PollingConfig;
}

授權

MIT Codeium