Eşzamansız işlevler, eş zamanlıymış gibi taahhüt tabanlı kodlar yazmanıza olanak tanır.
Eş zamansız işlevler Chrome, Edge, Firefox ve Safari'de varsayılan olarak etkindir ve gerçekten harikalar. Bunlar, vaadi temelli kod yazmanıza ana iş parçacığını engellemeden e-posta listesinden çıkabilirsiniz. Bunlar, eşzamansız kod daha az "zever" ve daha okunabilir olmasını sağlayın.
Eş zamansız işlevler şu şekilde çalışır:
async function myFirstAsyncFunction() {
try {
const fulfilledValue = await promise;
} catch (rejectedValue) {
// …
}
}
İşlev tanımından önce async
anahtar kelimesini kullanırsanız şunu kullanabilirsiniz:
await
değerini döndürür. Bir vaadi await
yaptığınızda işlev duraklatılır.
şekilde engellenmeyecek. Vaat yerine gelirse
değeri geri almaktır. Vaat reddedilirse reddedilen değer atılır.
Tarayıcı desteği
Örnek: bir getirme işlemini günlüğe kaydetme
Bir URL getirmek istediğinizi ve yanıtı metin olarak günlüğe kaydetmek istediğinizi düşünelim. Nasıl görünür? vaatleri kullanarak:
function logFetch(url) {
return fetch(url)
.then((response) => response.text())
.then((text) => {
console.log(text);
})
.catch((err) => {
console.error('fetch failed', err);
});
}
Burada da aynı şeyi eşzamansız işlevlerin kullanıldığı açıklayalım:
async function logFetch(url) {
try {
const response = await fetch(url);
console.log(await response.text());
} catch (err) {
console.log('fetch failed', err);
}
}
Aynı sayıda satır var ancak tüm geri çağırmalar kayboldu. Böylece, özellikle de vaatlere aşina olmayanlar için daha kolay okunuyor.
Eş zamansız döndürülen değerler
await
kullansanız da kullanmasanız da eşzamansız işlevler her zaman belirli bir değer döndürür. O
işlevi, eş zamansız işlevin döndürdüğü şeyle çözümlenir veya
eşzamansız işlevin attığı her şeyi. Dolayısıyla:
// wait ms milliseconds
function wait(ms) {
return new Promise((r) => setTimeout(r, ms));
}
async function hello() {
await wait(500);
return 'world';
}
...hello()
çağrısının yapılması, "world"
ile gerçekleştirileceği bir sözü döndürür.
async function foo() {
await wait(500);
throw Error('bar');
}
...foo()
çağrısı yapıldığında Error('bar')
ile beraber reddeden bir söz döndürülür.
Örnek: yanıt akışı sağlama
Daha karmaşık örneklerde eşzamansız işlevlerin avantajı daha da artar. İstediğinizi söyleyin (parçalardan çıkış yaparken yanıt akışı gerçekleştirebilir ve son boyutu döndürebilirsiniz).
Sözlerle şöyle:
function getResponseSize(url) {
return fetch(url).then((response) => {
const reader = response.body.getReader();
let total = 0;
return reader.read().then(function processResult(result) {
if (result.done) return total;
const value = result.value;
total += value.length;
console.log('Received chunk', value);
return reader.read().then(processResult);
});
});
}
Şuna bir göz atın: Jake "vaatlerin sahibi" Archibald. Nasıl aradığımı göster
processResult()
kendi içinde eşzamansız bir döngü oluşturmak için nasıl kullanılır? İyi bir yazı
kendimi çok akıllı hissediyorum. Ancak çoğu "akıllı" bir bakmışsınız, ona bakmanız gerekiyor.
ne yaptığını anlamak için birkaç yaşını doldurdu.
1990'lar.
Bunu eşzamansız işlevlerle tekrar deneyelim:
async function getResponseSize(url) {
const response = await fetch(url);
const reader = response.body.getReader();
let result = await reader.read();
let total = 0;
while (!result.done) {
const value = result.value;
total += value.length;
console.log('Received chunk', value);
// get the next result
result = await reader.read();
}
return total;
}
Tüm "akıllı" kayboldu. Kendimi bu kadar kötü hissettiren eş zamansız döngü
güven dolu, sıkıcı ve
süreç döngülerinden ibarettir. şimdi daha iyi oldu. Gelecekte,
eş zamansız yinelemeler
hangi
while
döngüsünü bir döngüyle değiştirerek daha düzenli hale getirin.
Diğer eşzamansız işlevin söz dizimi
Size daha önce async function() {}
anahtar kelimesini gösterdim ancak async
anahtar kelimesi
diğer işlev söz dizimiyle kullanılır:
Ok işlevleri
// map some URLs to json-promises
const jsonPromises = urls.map(async (url) => {
const response = await fetch(url);
return response.json();
});
Nesne yöntemleri
const storage = {
async getAvatar(name) {
const cache = await caches.open('avatars');
return cache.match(`/avatars/${name}.jpg`);
}
};
storage.getAvatar('jaffathecake').then(…);
Sınıf yöntemleri
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
const storage = new Storage();
storage.getAvatar('jaffathecake').then(…);
Dikkatli ol! Çok sıralı ifadeler kullanmaktan kaçının
Eşzamanlı görünen kod yazsanız da aynı kodu atlamadığınızdan emin olun paralel olarak yapmanız gereken şeylerdir.
async function series() {
await wait(500); // Wait 500ms…
await wait(500); // …then wait another 500ms.
return 'done!';
}
Yukarıdaki işlemin tamamlanması 1.000 ms sürer ancak:
async function parallel() {
const wait1 = wait(500); // Start a 500ms timer asynchronously…
const wait2 = wait(500); // …meaning this timer happens in parallel.
await Promise.all([wait1, wait2]); // Wait for both timers in parallel.
return 'done!';
}
Her iki bekleme işlemi aynı anda gerçekleştiğinden yukarıdaki işlemin tamamlanması 500 ms sürer. Pratik bir örneğe göz atalım.
Örnek: Getirme işlemlerini sırasıyla çıkarma
Diyelim ki bir dizi URL getiriyorsunuz ve bunları mümkün olan en kısa sürede emin olabilirsiniz.
Derin nefes - Vaat edilen sözler şu şekilde görünür:
function markHandled(promise) {
promise.catch(() => {});
return promise;
}
function logInOrder(urls) {
// fetch all the URLs
const textPromises = urls.map((url) => {
return markHandled(fetch(url).then((response) => response.text()));
});
// log them in order
return textPromises.reduce((chain, textPromise) => {
return chain.then(() => textPromise).then((text) => console.log(text));
}, Promise.resolve());
}
Doğru duydunuz, bir dizi vaat için reduce
kullanıyorum. Çok
akıllı. Ancak bu kod biraz çok akıllı olduğu için yazmadan daha iyi olacak.
Ancak, yukarıdakileri eş zamansız bir fonksiyona dönüştürürken çok sıralı:
async function logInOrder(urls) { for (const url of urls) { const response = await fetch(url); console.log(await response.text()); } }
function markHandled(...promises) { Promise.allSettled(promises); } async function logInOrder(urls) { // fetch all the URLs in parallel const textPromises = urls.map(async (url) => { const response = await fetch(url); return response.text(); }); markHandled(...textPromises); // log them in sequence for (const textPromise of textPromises) { console.log(await textPromise); } }
Tarayıcı desteği geçici çözümü: oluşturucular
Oluşturma araçları (örneğin, Tüm önemli tarayıcıların en yeni sürümü ) çoklu dolgu eşzamansız işlevleri sıralayabilirsiniz.
Babel bunu sizin için yapar, bu örnekte Babel REPL üzerinden bir örnek verilmiştir
- aktarılan kodun ne kadar benzer olduğuna dikkat edin. Bu dönüşüm, Babel'ın es2017 hazır ayarı.
Dönüştürme yaklaşımını kullanmanızı öneririm çünkü bu yaklaşımı hedef tarayıcılar eşzamansız işlevleri destekler, ancak bununla birlikte gerçekten bir aktarıyorsanız, Babel'ın çoklu dolgusu kendiniz kullanabilirsiniz. Şunun yerine:
async function slowEcho(val) {
await wait(1000);
return val;
}
...çoklu dolguyu dahil edersiniz ve şunu yazın:
const slowEcho = createAsyncFunction(function* (val) {
yield wait(1000);
return val;
});
createAsyncFunction
adlı iş ortağına bir oluşturucu (function*
) iletmeniz gerektiğini unutmayın.
ve await
yerine yield
kullanın. Bunun dışında aynı şekilde çalışır.
Geçici çözüm: yeniden oluşturucu
Eski tarayıcıları hedefliyorsanız, Babel ayrıca oluşturucuları, IE8'e kadar eş zamansız işlevleri kullanmanıza olanak tanır. Bunun için ihtiyacınız olanlar Babel'ın es2017 hazır ayarı ve es2015 hazır ayarı.
Çıktı pek hoş değil, bu yüzden çok iyi olur.
Her şeyi eş zamansız olarak yapın.
Eş zamansız işlevler tüm tarayıcılarda görüntülendiğinde, vaat veren işlev! Sadece kodunuzu daha düzenli hale getirmekle kalmaz, aynı zamanda işlevin her zaman bir vaat döndürdüğünden emin olun.
Asenkronize işlevler konusunda çok heyecanlıyım. 2014 ve bu canlıların web'de dolaştığını görmek harika. Hay aksi!