लंच एपीआई अनुरोध टाइमआउट?


106

मेरा एक fetch-api POSTनिवेदन है:

fetch(url, {
  method: 'POST',
  body: formData,
  credentials: 'include'
})

मैं जानना चाहता हूं कि इसके लिए डिफ़ॉल्ट टाइमआउट क्या है? और हम इसे 3 सेकंड या अनिश्चित सेकंड जैसे किसी विशेष मान पर कैसे सेट कर सकते हैं?

जवाबों:


78

संपादित करें 1

जैसा कि टिप्पणियों में कहा गया है, मूल उत्तर में कोड वादा / हल किए जाने के बाद भी टाइमर को चालू रखता है।

नीचे दिया गया कोड उस समस्या को हल करता है।

function timeout(ms, promise) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      reject(new Error('TIMEOUT'))
    }, ms)

    promise
      .then(value => {
        clearTimeout(timer)
        resolve(value)
      })
      .catch(reason => {
        clearTimeout(timer)
        reject(reason)
      })
  })
}


मूल उत्तर

इसमें एक निर्दिष्ट डिफ़ॉल्ट नहीं है; विनिर्देश बिल्कुल समयबाह्य चर्चा नहीं करता है।

आप सामान्य रूप से वादों के लिए अपना स्वयं का टाइमआउट आवरण लागू कर सकते हैं:

// Rough implementation. Untested.
function timeout(ms, promise) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      reject(new Error("timeout"))
    }, ms)
    promise.then(resolve, reject)
  })
}

timeout(1000, fetch('/hello')).then(function(response) {
  // process response
}).catch(function(error) {
  // might be a timeout error
})

में वर्णित है https://github.com/github/fetch/issues/175 द्वारा टिप्पणी https://github.com/mislav


28
यह स्वीकृत उत्तर क्यों है? अगर वादा ठान लिया तो यहां भी सेटटाइमआउट चलता रहेगा। एक बेहतर उपाय यह करना होगा: github.com/github/fetch/issues/175#issuecomment-216791333
radtad

3
@ श्राद्ध मिथक अपने दृष्टिकोण को उस धागे में कम करता है: github.com/github/fetch/issues/175#issuecomment-284787564 । इससे कोई फर्क नहीं पड़ता कि समय समाप्त हो रहा है, क्योंकि .reject()पहले से ही हल किए गए वादे पर कॉल करने से कुछ नहीं होता है।
मार्क अमेरी

1
हालाँकि 'fetch' फ़ंक्शन को टाइमआउट द्वारा अस्वीकार कर दिया गया है, लेकिन पृष्ठभूमि tcp कनेक्शन बंद नहीं है। मैं अपनी नोड प्रक्रिया को इनायत से कैसे छोड़ सकता हूं?
प्रोग क्वार्टर

28
रुकें! यह गलत उत्तर है! हालाँकि, यह एक अच्छे और काम करने वाले समाधान की तरह दिखता है, लेकिन वास्तव में कनेक्शन बंद नहीं होगा, जो अंततः टीसीपी कनेक्शन पर कब्जा कर लेता है (अनंत भी हो सकता है - सर्वर पर निर्भर करता है)। इस गलत समाधान की कल्पना करें कि एक ऐसी प्रणाली को लागू किया जाए जो हर समय एक कनेक्शन को बनाए रखता है - इससे नेटवर्क इंटरफ़ेस घुटन (ओवरलोडिंग) हो सकता है और अंततः आपकी मशीन लटका सकती है! @ और बिना सही उत्तर दिए यहां पोस्ट किया ।
स्लाविक मेल्टसर

2
@SlavikMeltser मुझे नहीं मिला। आपके द्वारा बताया गया उत्तर टीसीपी कनेक्शन को नहीं तोड़ता है।
मेटुस पाइरेक्ट्स

147

मैं वास्तव में Promise.race का उपयोग करके इस जिस्ट से स्वच्छ दृष्टिकोण पसंद करता हूं

fetchWithTimeout.js

export default function (url, options, timeout = 7000) {
    return Promise.race([
        fetch(url, options),
        new Promise((_, reject) =>
            setTimeout(() => reject(new Error('timeout')), timeout)
        )
    ]);
}

main.js

import fetch from './fetchWithTimeout'

// call as usual or with timeout as 3rd argument

fetch('http://google.com', options, 5000) // throw after max 5 seconds timeout error
.then((result) => {
    // handle result
})
.catch((e) => {
    // handle errors and timeout error
})

2
यदि टाइमआउट के बाद कोई fetchत्रुटि होती है तो यह "अनहेल्ड रिजेक्शन" का कारण बनता है । यह समय-समय पर नहीं हुआ है, तो विफलता ( और असफलता ) को संभालने से हल किया जा सकता है । .catchfetch
सिंहल

7
IMHO इसे निरस्त किया जा सकता है जब निरस्त करते समय AbortController के साथ, देखें stackoverflow.com/a/47250621
RiZKiT

यदि समय के साथ-साथ सफल होता है, तो टाइमआउट को साफ करना बेहतर होगा।
Bob9630

116

एक वादा दौड़ समाधान का उपयोग करते हुए अनुरोध लटका हुआ है और अभी भी पृष्ठभूमि में बैंडविड्थ का उपभोग करेगा और प्रक्रिया में होने के दौरान अधिकतम अनुमत समवर्ती अनुरोध कम किया जाएगा।

इसके बजाय वास्तव में अनुरोध को निरस्त करने के लिए एबोर्टकंट्रोलर का उपयोग करें , यहां एक उदाहरण है

const controller = new AbortController()

// 5 second timeout:
const timeoutId = setTimeout(() => controller.abort(), 5000)

fetch(url, { signal: controller.signal }).then(response => {
  // completed request before timeout fired

  // If you only wanted to timeout the request, not the response, add:
  // clearTimeout(timeoutId)
})

एबोर्टकंट्रोलर का उपयोग अन्य चीजों के लिए भी किया जा सकता है, न केवल लाने के लिए बल्कि पठनीय / लिखने योग्य धाराओं के लिए भी। अधिक नए कार्य (विशेष रूप से वादा आधारित) यह अधिक से अधिक उपयोग करेंगे। NodeJS ने अपनी धाराओं / फाइल सिस्टम में भी AbortController को लागू किया है। मुझे पता है कि वेब ब्लूटूथ इसे भी देख रहा है


16
यह वादा-दौड़-समाधान से भी बेहतर लगता है क्योंकि यह संभवतः पहले की प्रतिक्रिया लेने के बजाय अनुरोध को रद्द कर देता है। यदि मैं गलत हूं तो मुझे सही करों।
कार्ल एडलर

3
उत्तर यह नहीं समझाता है कि AbortController क्या है। इसके अलावा, यह प्रायोगिक है और इसे असमर्थित इंजनों में पॉलीफ़िल किया जाना चाहिए, यह भी वाक्यविन्यास नहीं है।
एस्टुस फ्लास्क

यह स्पष्ट नहीं कर सकता है कि एबॉर्टकंट्रोलर क्या है (मैंने आलसी लोगों के लिए इसे आसान बनाने के लिए उत्तर के लिए एक लिंक जोड़ा), लेकिन यह अब तक का सबसे अच्छा जवाब है, क्योंकि यह इस तथ्य को उजागर करता है कि केवल एक अनुरोध को अनदेखा करने का मतलब यह नहीं है लंबित नहीं है। बहुत बढ़िया जवाब।
औरेलियो

2
"मैंने आलसी लोगों के लिए इसे आसान बनाने के लिए उत्तर के लिए एक लिंक जोड़ा" - यह वास्तव में नियमों के अनुसार एक लिंक और अधिक जानकारी के साथ आना चाहिए। लेकिन उत्तर में सुधार के लिए धन्यवाद।
जय विक

8
इस जवाब का जवाब न देने से बेहतर है क्योंकि लोग नाइटपिक्री, टीबीएच द्वारा बंद कर दिए गए हैं
माइकल टेरी

26

अंतहीन 'उत्कृष्ट जवाब पर निर्माण , मैंने एक उपयोगी उपयोगिता फ़ंक्शन बनाया।

const fetchTimeout = (url, ms, { signal, ...options } = {}) => {
    const controller = new AbortController();
    const promise = fetch(url, { signal: controller.signal, ...options });
    if (signal) signal.addEventListener("abort", () => controller.abort());
    const timeout = setTimeout(() => controller.abort(), ms);
    return promise.finally(() => clearTimeout(timeout));
};
  1. यदि संसाधन प्राप्त करने से पहले समय समाप्त हो जाता है, तो भ्रूण का गर्भपात हो जाता है।
  2. यदि समय समाप्त होने से पहले संसाधन प्राप्त किया जाता है तो समय समाप्त हो जाता है।
  3. यदि इनपुट सिग्नल को निरस्त किया जाता है तो भ्रूण का गर्भपात हो जाता है और समय समाप्त हो जाता है।
const controller = new AbortController();

document.querySelector("button.cancel").addEventListener("click", () => controller.abort());

fetchTimeout("example.json", 5000, { signal: controller.signal })
    .then(response => response.json())
    .then(console.log)
    .catch(error => {
        if (error.name === "AbortError") {
            // fetch aborted either due to timeout or due to user clicking the cancel button
        } else {
            // network error or json parsing error
        }
    });

उम्मीद है की वो मदद करदे।


2
यह शानदार है! इसमें सभी गंदे किनारे वाले मामले शामिल हैं जो अन्य उत्तरों में समस्याग्रस्त थे, और आप एक स्पष्ट उपयोग उदाहरण प्रदान करते हैं।
जुवोनेन

8

अभी तक एपीआई एपीआई में कोई समयबाह्य समर्थन नहीं है। लेकिन इसे वादे में लपेट कर हासिल किया जा सकता था।

उदाहरण के लिए।

  function fetchWrapper(url, options, timeout) {
    return new Promise((resolve, reject) => {
      fetch(url, options).then(resolve, reject);

      if (timeout) {
        const e = new Error("Connection timed out");
        setTimeout(reject, timeout, e);
      }
    });
  }

मुझे यह एक अच्छा लगता है, एक से अधिक बार उपयोग करने के लिए कम दोहराव वाला।
डंडविस

1
अनुरोध, टाइमआउट के बाद रद्द नहीं किया गया है, सही है? यह ओपी के लिए ठीक हो सकता है, लेकिन कभी-कभी आप एक अनुरोध क्लाइंट-साइड को रद्द करना चाहते हैं।
कोशिश

2
@ क्रिसिस अच्छी तरह से, हाँ। हाल ही में AbortController के साथ गर्भपात के लिए एक समाधान लागू किया गया है , लेकिन सीमित ब्राउज़र समर्थन के साथ अभी भी प्रयोगात्मक है। चर्चा
कोड-जफ

यह मज़ेदार है, IE और Edge ही इसका समर्थन करते हैं! जब तक मोबाइल मोज़िला साइट को फिर से काम कर रहा है ...
trysis

फ़ायरफ़ॉक्स 57 से इसका समर्थन कर रहा है। :: क्रोम में देखना ::
फ्रैंकलिन यू

6

संपादित करें : बैकग्राउंड रिक्वेस्ट अभी भी बैकग्राउंड में चल रही होगी और संभवतः आपके कंसोल में एक एरर लॉग करेगी।

वास्तव में Promise.raceदृष्टिकोण बेहतर है।

संदर्भ के लिए यह लिंक देखें Promise.race ()

रेस का मतलब है कि सभी वादे एक ही समय पर चलेंगे, और जैसे ही कोई एक वादा वापस करता है, रेस बंद हो जाएगी। इसलिए, केवल एक मान लौटाया जाएगा । आप यह भी कह सकते हैं कि यदि भ्रूण समय से बाहर हो तो कॉल करने के लिए एक फ़ंक्शन पारित कर सकता है।

fetchWithTimeout(url, {
  method: 'POST',
  body: formData,
  credentials: 'include',
}, 5000, () => { /* do stuff here */ });

यदि यह आपकी रुचि को प्रभावित करता है, तो एक संभावित कार्यान्वयन होगा:

function fetchWithTimeout(url, options, delay, onTimeout) {
  const timer = new Promise((resolve) => {
    setTimeout(resolve, delay, {
      timeout: true,
    });
  });
  return Promise.race([
    fetch(url, options),
    timer
  ]).then(response => {
    if (response.timeout) {
      onTimeout();
    }
    return response;
  });
}

1

आप टाइमआउटप्रॉपर रैपर बना सकते हैं

function timeoutPromise(timeout, err, promise) {
  return new Promise(function(resolve,reject) {
    promise.then(resolve,reject);
    setTimeout(reject.bind(null,err), timeout);
  });
}

फिर आप किसी भी वादे को पूरा कर सकते हैं

timeoutPromise(100, new Error('Timed Out!'), fetch(...))
  .then(...)
  .catch(...)  

यह वास्तव में एक अंतर्निहित कनेक्शन को रद्द नहीं करेगा लेकिन आपको एक वादा पूरा करने की अनुमति देगा।
संदर्भ


1

यदि आपने अपने कोड में टाइमआउट कॉन्फ़िगर नहीं किया है, तो यह आपके ब्राउज़र का डिफ़ॉल्ट अनुरोध टाइमआउट होगा।

1) फ़ायरफ़ॉक्स - 90 सेकंड

about:configफ़ायरफ़ॉक्स URL फ़ील्ड में टाइप करें। कुंजी के अनुरूप मान ज्ञात कीजिएnetwork.http.connection-timeout

2) क्रोम - 300 सेकंड

स्रोत


0
  fetchTimeout (url,options,timeout=3000) {
    return new Promise( (resolve, reject) => {
      fetch(url, options)
      .then(resolve,reject)
      setTimeout(reject,timeout);
    })
  }

यह बहुत अधिक stackoverflow.com/a/46946588/1008999 के समान है, लेकिन आपके पास डिफ़ॉल्ट समय समाप्ति है
अंतहीन

-1

का उपयोग करते हुए सी promise2 रद्द lib टाइमआउट साथ लाने इस एक (कैसा लग सकता है लाइव jsfiddle डेमो ):

import CPromise from "c-promise2"; // npm package

function fetchWithTimeout(url, {timeout, ...fetchOptions}= {}) {
    return new CPromise((resolve, reject, {signal}) => {
        fetch(url, {...fetchOptions, signal}).then(resolve, reject)
    }, timeout)
}
        
const chain = fetchWithTimeout("https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=10s", {timeout: 5000})
    .then(request=> console.log('done'));
    
// chain.cancel(); - to abort the request before the timeout

यह कोड एक npm पैकेज cp-fetch के रूप में है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.