फ़ेच करने की अनुमति रद्द की जा सकती है

"फ़ेच रद्द करना" के लिए GitHub की मूल समस्या यह थी 2015 में खोला गया. अब, अगर मैं 2017 (मौजूदा साल) से 2015 को दूर ले जाऊं, तो मुझे 2 मिलेगा. इससे पता चलता है कि गड़बड़ी हुई, क्योंकि 2015 असल में "हमेशा के लिए" था पहले से है.

साल 2015 में, हमने पहली बार फ़ेच किए जा रहे अनुरोधों को रद्द करने की प्रक्रिया शुरू की. 780 GitHub टिप्पणियों के बाद, कुछ गलत शुरुआत हुई और पांच पुल अनुरोध मिले, आखिर में ब्राउज़र में हमें फ़ेच लैंडिंग पेज मिल गया, जिसे बार-बार ट्रैक नहीं किया जा सकता, पहला वर्शन Firefox 57 में होने वाला है.

अपडेट करें: नहीं, मैं गलत थी. Edge 16 सबसे पहले अब गर्भपात की सुविधा के साथ उपलब्ध है! बधाई हो एज टीम!

मैं इतिहास के बारे में बाद में जानकारी दूंगा, लेकिन पहले, एपीआई के बारे में:

कंट्रोलर और सिग्नल की गतिविधि

AbortController और AbortSignal के बारे में जानें:

const controller = new AbortController();
const signal = controller.signal;

कंट्रोलर में सिर्फ़ एक तरीका होता है:

controller.abort();

ऐसा करने पर, सिग्नल को सूचना दी जाती है:

signal.addEventListener('abort', () => {
    // Logs true:
    console.log(signal.aborted);
});

यह एपीआई DOM स्टैंडर्ड से मिला है और यही पूरा एपीआई है. यह समय है यह जान-बूझकर जेनरिक है. इसलिए, दूसरे वेब स्टैंडर्ड और JavaScript लाइब्रेरी में इसका इस्तेमाल किया जा सकता है.

सिग्नल रद्द करें और फ़ेच करें

फ़ेच करने में AbortSignal लग सकता है. उदाहरण के लिए, यहां बताया गया है कि 5 सेकंड के बाद फ़ेच करने का समय कैसे तय किया जा सकता है सेकंड:

const controller = new AbortController();
const signal = controller.signal;

setTimeout(() => controller.abort(), 5000);

fetch(url, { signal }).then(response => {
    return response.text();
}).then(text => {
    console.log(text);
});

फ़ेच करने की प्रोसेस रद्द करने पर, अनुरोध और रिस्पॉन्स, दोनों रद्द हो जाते हैं. इससे रिस्पॉन्स के मुख्य हिस्से को पढ़ने की सुविधा मिलती है (जैसे कि response.text()) भी निरस्त किया जाता है.

यह रहा एक डेमो – लिखने के समय, सिर्फ़ ब्राउज़र पर ही जो Firefox 57 का समर्थन करता है. साथ ही, खुद को तैयार रखें. डिज़ाइन करने में माहिर व्यक्ति ने आपकी मदद नहीं की बहुत कम समय लगता है.

इसके अलावा, किसी अनुरोध ऑब्जेक्ट को सिग्नल दिया जा सकता है और बाद में इसे फ़ेच करने के लिए पास किया जा सकता है:

const controller = new AbortController();
const signal = controller.signal;
const request = new Request(url, { signal });

fetch(request);

यह इसलिए काम करता है, क्योंकि request.signal एक AbortSignal है.

रद्द किए गए फ़ेच पर प्रतिक्रिया देना

जब एसिंक्रोनस कार्रवाई रद्द की जाती है, तो AbortError नाम के DOMException के साथ प्रॉमिस अस्वीकार हो जाता है:

fetch(url, { signal }).then(response => {
    return response.text();
}).then(text => {
    console.log(text);
}).catch(err => {
    if (err.name === 'AbortError') {
    console.log('Fetch aborted');
    } else {
    console.error('Uh oh, an error!', err);
    }
});

अगर उपयोगकर्ता कार्रवाई को रद्द करता है, तो आपको आम तौर पर गड़बड़ी का मैसेज नहीं दिखाना चाहिए, क्योंकि यह कार्रवाई को रद्द नहीं करता "गड़बड़ी" उपयोगकर्ता की उम्मीद के मुताबिक काम करें. इससे बचने के लिए, अगर-स्टेटमेंट का इस्तेमाल करें, जैसे कि खास तौर पर, गर्भपात से जुड़ी गड़बड़ियों को हैंडल करने के लिए.

यहां एक उदाहरण दिया गया है, जिसमें उपयोगकर्ता को कॉन्टेंट लोड करने के लिए बटन दिया गया है और रद्द करने के लिए एक बटन दिया गया है. अगर फ़ेच गड़बड़ियां दिखती हैं, तो एक गड़बड़ी दिखाई जाती है. जब तक कि यह कार्रवाई रद्द करने से जुड़ी गड़बड़ी न हो:

// This will allow us to abort the fetch.
let controller;

// Abort if the user clicks:
abortBtn.addEventListener('click', () => {
    if (controller) controller.abort();
});

// Load the content:
loadBtn.addEventListener('click', async () => {
    controller = new AbortController();
    const signal = controller.signal;

    // Prevent another click until this fetch is done
    loadBtn.disabled = true;
    abortBtn.disabled = false;

    try {
    // Fetch the content & use the signal for aborting
    const response = await fetch(contentUrl, { signal });
    // Add the content to the page
    output.innerHTML = await response.text();
    }
    catch (err) {
    // Avoid showing an error message if the fetch was aborted
    if (err.name !== 'AbortError') {
        output.textContent = "Oh no! Fetching failed.";
    }
    }

    // These actions happen no matter how the fetch ends
    loadBtn.disabled = false;
    abortBtn.disabled = true;
});

यहां एक डेमो दिया गया है – लिखते समय, सिर्फ़ ऐसे ब्राउज़र यह Edge 16 और Firefox 57 पर काम करता है.

एक सिग्नल, कई फ़ेच

एक ही सिग्नल का इस्तेमाल करके, एक साथ कई फ़ेच रद्द किए जा सकते हैं:

async function fetchStory({ signal } = {}) {
    const storyResponse = await fetch('/story.json', { signal });
    const data = await storyResponse.json();

    const chapterFetches = data.chapterUrls.map(async url => {
    const response = await fetch(url, { signal });
    return response.text();
    });

    return Promise.all(chapterFetches);
}

ऊपर दिए गए उदाहरण में, शुरुआती फ़ेच और पैरलल चैप्टर के लिए एक ही सिग्नल का इस्तेमाल किया गया है फ़ेच. यहां बताया गया है कि fetchStory को इस्तेमाल करने का तरीका क्या है:

const controller = new AbortController();
const signal = controller.signal;

fetchStory({ signal }).then(chapters => {
    console.log(chapters);
});

इस स्थिति में, controller.abort() को कॉल करने से जो भी फ़ेच किए जा रहे हैं उन्हें रद्द कर दिया जाएगा.

आने वाला समय

अन्य ब्राउज़र

Edge ने इसे पहले शिप करने के लिए बहुत अच्छा काम किया था और Firefox इस काम में सबसे ज़्यादा चर्चित है. उनके इंजीनियर test suite से लागू किया गया, जबकि स्पेसिफ़िकेशन लिखा जा रहा है. अन्य ब्राउज़र के लिए, टिकट यहां दिए गए हैं:

सर्विस वर्कर में

मुझे सर्विस वर्कर पुर्ज़ों के लिए ज़रूरी शर्तें पूरी करनी हैं, लेकिन यह रहा प्लान:

जैसा कि मैंने पहले बताया, हर Request ऑब्जेक्ट में signal प्रॉपर्टी होती है. सर्विस वर्कर में, अगर पेज ने अब जवाब में दिलचस्पी नहीं दिखाई है, तो fetchEvent.request.signal इसे रद्द करने का सिग्नल देगा. इस वजह से, इस तरह का कोड काम करता है:

addEventListener('fetch', event => {
    event.respondWith(fetch(event.request));
});

अगर पेज फ़ेच करने की प्रोसेस को रद्द करता है, तो fetchEvent.request.signal सिग्नल रद्द हो जाता है, इसलिए फ़ेच करने की प्रोसेस सर्विस वर्कर भी रद्द करते हैं.

अगर आपको event.request के बजाय कोई दूसरा सिग्नल फ़ेच करना है, तो आपको अपने पसंद के मुताबिक फ़ेच करना.

addEventListener('fetch', event => {
    const url = new URL(event.request.url);

    if (event.request.method == 'GET' && url.pathname == '/about/') {
    // Modify the URL
    url.searchParams.set('from-service-worker', 'true');
    // Fetch, but pass the signal through
    event.respondWith(
        fetch(url, { signal: event.request.signal })
    );
    }
});

इसे ट्रैक करने के लिए विनिर्देशों का पालन करें – मैं यहां लिंक जोड़ दूंगा लागू करने के लिए तैयार होने पर ब्राउज़र टिकट.

इतिहास

हां... इस तुलना में आसान एपीआई को एक साथ लाने में बहुत समय लगा. यहां इसकी वजह दी गई है:

एपीआई से जुड़े असहमति

जैसा कि आपको दिख रहा है, GitHub पर चर्चा काफ़ी लंबी है. इस थ्रेड में बहुत सारी बारीकियां हैं और कुछ हद तक बारीकियां भी हैं. हालांकि, सबसे अहम बात यह है कि ग्रुप चाहता था कि fetch() से लौटाए गए ऑब्जेक्ट पर, abort तरीका मौजूद रहे, जबकि दूसरा तरीका जवाब पाने और प्रतिक्रिया पर असर डालने के बीच अंतर चाहते थे.

ये ज़रूरी शर्तें काम नहीं करती हैं. इसलिए, एक ग्रुप को वह नहीं मिल रहा था जो वह चाहता था. अगर ऐसा है माफ़ करें! अगर आपको अच्छा महसूस हो, तो मैं भी उस ग्रुप में शामिल थी. हालांकि, अगर मुझे लगता है कि AbortSignal, की शर्तों का इस्तेमाल करने से यह सही विकल्प लगता है. साथ ही, बार-बार किए जाने वाले वादों को हो सकता है कि उनका मक़सद, घटने-बढ़ने की समस्या हो.

अगर आपको कोई ऐसा ऑब्जेक्ट वापस करना है जो जवाब देता है, लेकिन रद्द भी कर सकता है, तो सिंपल रैपर:

function abortableFetch(request, opts) {
    const controller = new AbortController();
    const signal = controller.signal;

    return {
    abort: () => controller.abort(),
    ready: fetch(request, { ...opts, signal })
    };
}

टीसीएफ़, TC39 से शुरू होता है

रद्द की गई कार्रवाई को किसी गड़बड़ी से अलग दिखाने की कोशिश की गई थी. इसमें तीसरा प्रॉमिस भी शामिल है स्टेटस "रद्द किया गया" दिखाता है. साथ ही, सिंक और एक साथ काम न करने वाली प्रोसेस, दोनों में रद्द किए जाने की प्रक्रिया को मैनेज करने के लिए कुछ नए सिंटैक्स कोड:

यह न करें

कोड असली नहीं है — प्रस्ताव वापस ले लिया गया

    try {
      // Start spinner, then:
      await someAction();
    }
    catch cancel (reason) {
      // Maybe do nothing?
    }
    catch (err) {
      // Show error message
    }
    finally {
      // Stop spinner
    }

किसी कार्रवाई को रद्द किए जाने के बाद, आम तौर पर कुछ भी नहीं किया जाता है. ऊपर दिए गए प्रस्ताव को अलग कर दिया गया है रद्द करने की प्रक्रिया पूरी कर देनी चाहिए, ताकि आपको खास तौर पर गर्भपात करने से जुड़ी गड़बड़ियों को हैंडल करने की ज़रूरत न पड़े. catch cancel ने लेट तो आपको रद्द की गई कार्रवाइयों के बारे में पता चलता है, लेकिन ज़्यादातर मामलों में आपको इनकी ज़रूरत ही नहीं होती.

TC39 में इसे स्टेज 1 में लाया गया था, लेकिन सभी की सहमति नहीं बनी और प्रपोज़ल को वापस ले लिया गया.

हमारे दूसरे प्रस्ताव, AbortController में किसी नए सिंटैक्स की ज़रूरत नहीं है. इसलिए, यह समझ में नहीं आया TC39 के तहत इसकी शर्तें पूरी करेंगे. JavaScript से हमें जो कुछ भी चाहिए वह पहले से मौजूद था, इसलिए हमने इंटरफ़ेस, खास तौर पर DOM स्टैंडर्ड के हिसाब से बनाए गए होंगे. यह फ़ैसला लेने के बाद, बाकी काम काफ़ी तेज़ी से पूरे हो गए.

स्पेसिफ़िकेशन में ज़्यादा बदलाव किया गया

XMLHttpRequest सालों से ऐसा नहीं किया जा सका, लेकिन इसकी जानकारी साफ़ तौर पर नहीं दी गई थी. इस समय इस बारे में साफ़ तौर पर जानकारी नहीं थी इससे पता चलता है कि किस तरह की नेटवर्क गतिविधि से बचा जा सकता है या उसे खत्म किया जा सकता है. इसके अलावा, अगर abort() को कॉल करने और फ़ेच करने के बीच एक रेस कंडिशन थी.

हम चाहते थे कि इस बार सही जवाब दिया जाए, लेकिन इसकी वजह से खास समीक्षा करना (यह मेरी गलती है. इसके लिए ऐन वैन केस्टेरेन को बहुत-बहुत धन्यवाद और डॉमिनिक डेनिकोला और एक अच्छा टेस्ट सेट किया.

लेकिन अभी हम यहां हैं! हमारे पास एक नया वेब प्रिमिटिव है जो एक साथ काम नहीं करने वाली कार्रवाइयों को रद्द करता है. इससे कई फ़ाइलें फ़ेच की जा सकती हैं कंट्रोल किया जा सकता है! आने वाले समय में, हम फ़ेच की पूरी अवधि और उससे बड़े लेवल पर प्राथमिकता में बदलाव को चालू करने पर चर्चा करेंगे फ़ीड पाने की प्रोसेस की प्रोग्रेस को देखने के लिए, एपीआई.