"Getirme işlemini iptal etme" ile ilgili orijinal GitHub sorunu şuydu: 2015’te açıldı. 2015 ile 2017'nin (mevcut yıl) farkına varırsam 2 puan alıyorum. Bu durum, matematik hatası, çünkü 2015 aslında "sürekli" geçti. önce.
2015 yılında, devam eden getirme işlemlerini iptal etmeyi keşfetmeye başladığımızda ilk kez 780 GitHub yorumundan sonra çok sayıda yanlış başlatma ve 5 pull isteği içerir. Son olarak, tarayıcılarda İlki Firefox 57.
Güncelleme: Hayır, yanılmışım. Edge 16'da önce iptal desteği verildi. Başarılı bir proje için Edge ekibi!
Geçmişi daha sonra ele alacağım, ama önce API'yla ilgili olarak:
Kumanda ve sinyal manevrası
AbortController
ve AbortSignal
ile tanışın:
const controller = new AbortController();
const signal = controller.signal;
Kumandanın yalnızca bir yöntemi vardır:
controller.abort();
Bunu yaptığınızda sinyali bilgilendirir:
signal.addEventListener('abort', () => {
// Logs true:
console.log(signal.aborted);
});
Bu API, DOM standardı tarafından sağlanır ve API'nin tamamı budur. İnsanların diğer web standartları ve JavaScript kitaplıkları tarafından kullanılabilecek şekilde kasıtlı olarak genel olmalıdır.
Sinyalleri iptal et ve getir
Getirme işlemi AbortSignal
alabilir. Örneğin, 5 saniye sonra getirme zaman aşımını şu şekilde
saniye:
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);
});
Bir getirme işlemini iptal ettiğinizde hem isteği hem de yanıtı iptal eder. Dolayısıyla, yanıt gövdesinin tüm okumaları da
(response.text()
gibi) da iptal edildi.
Bu demo – Yazıldığı sırada tek tarayıcı Firefox 57'yi destekler. Ayrıca, arkanıza yaslanın. Tasarım becerisi olan kimse katılmadı. demoyu göz önünde bulundurun.
Alternatif olarak, sinyal bir istek nesnesine verilebilir ve daha sonra getirme işlemi için iletilebilir:
const controller = new AbortController();
const signal = controller.signal;
const request = new Request(url, { signal });
fetch(request);
request.signal
bir AbortSignal
olduğundan bu yöntem işe yarar.
İptal edilen bir getirme işlemine tepki verme
Eş zamansız bir işlemi iptal ettiğinizde, taahhüt AbortError
adlı bir DOMException
ile reddedilir:
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);
}
});
Kullanıcı işlemi iptal ettiyse hata mesajının gösterilmesini genellikle istemezsiniz, çünkü bu bir "hata" kullanıcının isteğini başarıyla yerine getirirseniz. Bunu önlemek için bir örneği inceleyin.
Kullanıcıya içerik yüklemesi için bir düğme ve iptal etmek için bir düğme sağlayan bir örneği burada görebilirsiniz. Getirme iptal etme hatası olmadığı sürece bir hata gösterilir:
// 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;
});
.
Burada bir demo verilmiştir – Yazmaya başladığınızda yalnızca Edge 16 ve Firefox 57 desteklenir.
Tek sinyal, birçok getirme
Birçok getirmeyi tek seferde iptal etmek için tek bir sinyal kullanılabilir:
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);
}
Yukarıdaki örnekte, ilk getirme ve paralel bölüm için aynı sinyal kullanılmaktadır.
getirir. fetchStory
aracını şu şekilde kullanırsınız:
const controller = new AbortController();
const signal = controller.signal;
fetchStory({ signal }).then(chapters => {
console.log(chapters);
});
Bu durumda, controller.abort()
işlevinin çağrılması devam eden getirme işlemlerini iptal eder.
Gelecek
Diğer tarayıcılar
Edge bu modeli ilk göndermek için harika bir iş çıkardı ve Firefox bu konuda son derece başarılı. Mühendisleri test paketinden uygulanmıştır. yazmaktan ibaret değildir. Diğer tarayıcılar için ise izlemeleri gereken biletler şunlardır:
Service Worker'da
Servis çalışanı parçaları için spesifikasyonları tamamlamam gerekiyor ancak planı şu şekilde:
Daha önce de belirttiğim gibi, her Request
nesnesinin bir signal
özelliği vardır. Service Worker içinde,
fetchEvent.request.signal
, sayfa artık yanıtla ilgilenmiyorsa iptal sinyalini verir.
Sonuç olarak, şuna benzer bir kod işe yarar:
addEventListener('fetch', event => {
event.respondWith(fetch(event.request));
});
Sayfa getirmeyi iptal ederse fetchEvent.request.signal
sinyali iptal eder. Dolayısıyla,
hizmet çalışanı tarafından da iptal edilir.
event.request
dışında bir şey getiriyorsanız sinyali
özel getirmeler olduğundan emin olun.
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 })
);
}
});
Bunu takip etmek için spesifikasyonları uygulayın. Talimatlara tarayıcı biletleri uygulanır.
Tarih
Evet... Bu nispeten basit API'nin bir araya gelmesi çok uzun sürdü. Bunun nedenleri aşağıda açıklanmıştır:
API uyuşmazlığı
Gördüğünüz gibi GitHub tartışması oldukça uzun.
Bu mesaj dizisinde çok sayıda nüans vardır (ve herhangi bir nüans yoktur) ama temel anlaşmazlık şudur:
grubu, abort
yönteminin fetch()
tarafından döndürülen nesnede mevcut olmasını isterken diğer
tepkiyi almakla etkilemek arasında bir ayrım olmasını talep ediyordu.
Bu gereksinimler uyumsuz olduğundan bir grup istediği elde edemiyordu. Eğer
özür dileriz! Daha iyi hissetmeni sağlıyorsa ben de o grupta yer aldım. Ancak AbortSignal
bu durum doğru bir seçim gibi görünmesine neden oluyor. Ayrıca, zincirleme vaatlere izin vermek
imkansız değilse çok karmaşık hale gelebilir.
Yanıt sağlayan ancak iptal edebilen bir nesne döndürmek istiyorsanız bir nesne oluşturabilirsiniz. basit sarmalayıcı:
function abortableFetch(request, opts) {
const controller = new AbortController();
const signal = controller.signal;
return {
abort: () => controller.abort(),
ready: fetch(request, { ...opts, signal })
};
}
TC39'da yanlış başlıyor
İptal edilmiş bir işlemi hatadan ayırt etmek için çaba gösterilmesi. Buna üçüncü bir söz, "iptal edildi"yi belirtmek için durum ve hem senkronizasyon hem de eşzamansız iptal işlemleri için bazı yeni söz dizimi kod:
Gerçek kod değil — teklif geri çekildi
try { // Start spinner, then: await someAction(); } catch cancel (reason) { // Maybe do nothing? } catch (err) { // Show error message } finally { // Stop spinner }
Bir işlem iptal edildiğinde yapılacak en yaygın işlem hiçbir şey yapmamaktır. Yukarıdaki teklif ayrıldı
sayesinde, özellikle iptal hatalarını ele almanız gerekmiyor. catch cancel
izin
ancak çoğu zaman buna ihtiyaç duymazsınız.
TC39'da bu konuda 1. aşamaya geçmiş ancak fikir birliği sağlanamadı ve teklif geri çekildi.
Alternatif teklifimiz (AbortController
) yeni bir söz dizimi gerektirmediğinden anlamlı değildi
bunu TC39 kapsamında denetliyoruz. JavaScript'ten ihtiyacımız olan her şey zaten mevcuttu. Bu nedenle,
DOM standardı olmak üzere, web platformundaki arayüzler için de geçerlidir. Bu kararı verdikten sonra
diğerleri görece hızlı bir şekilde bir araya geldi.
Büyük özellik değişikliği
XMLHttpRequest
yıllardır iptal edilmişti ancak spesifikasyon son derece belirsizdi. Şu saatte açık değildi:
temel ağ etkinliğinden kaçınabileceğinizi veya sonlandırılabileceğini ya da
abort()
çağrılması ve getirme işleminin tamamlanması arasında bir yarış koşulu vardı.
Bu defa doğru yapmak istiyorduk. Ancak bu da büyük bir özellik değişikliğiyle sonuçlandı ve çok fazla (bu benim hatam. Anne van Kesteren ve ekibim sayesinde Domenic Denicola) ve yeterli bir test grubu.
Ama artık buradayız! Eş zamansız işlemleri iptal etmek için yeni bir temel web bileşenimiz var ve birden fazla getirme işlemi birden fazla aynı anda kontrol edilebilir! İlerleyen zamanlarda, bir getirme sürecinin ömrü boyunca öncelik değişikliklerinin nasıl etkinleştirileceğini ve daha yüksek bir düzeydeki Getirme işleminin ilerleme durumunu gözlemlemek için API.