Asynchronize işlevler, promise tabanlı kodları zaman uyumluymuş gibi yazmanıza olanak tanır.
Asenkron işlevler Chrome, Edge, Firefox ve Safari'de varsayılan olarak etkindir ve gerçekten muhteşemdir. Bu işlevler, ana iş parçacığı engellenmeden, söze dayalı kodları sanki senkronizeymiş gibi yazmanıza olanak tanır. Bunlar, eşzamansız kodunuzu daha az "zekice" ve daha okunabilir hale getirir.
Eş zamansız işlevler şu şekilde çalışır:
async function myFirstAsyncFunction() {
try {
const fulfilledValue = await promise;
} catch (rejectedValue) {
// …
}
}
Bir işlev tanımından önce async
anahtar kelimesini kullanırsanız işlev içinde await
kullanabilirsiniz. Bir vaadi await
istediğinizde işlev, vaat yerine gelene kadar engellemeyen bir şekilde duraklatılır. Söz verilen işlem gerçekleşirse değeri geri alırsınız. Sözleşme reddedilirse reddedilen değer atılır.
Tarayıcı desteği
Örnek: bir getirme işlemini günlüğe kaydetme
Bir URL almak ve yanıtı metin olarak günlüğe kaydetmek istediğinizi varsayalım. Söz vererek bu işlemi aşağıdaki gibi yapabilirsiniz:
function logFetch(url) {
return fetch(url)
.then((response) => response.text())
.then((text) => {
console.log(text);
})
.catch((err) => {
console.error('fetch failed', err);
});
}
Aşağıda, aynı işlemler asynkron işlevler kullanılarak gösterilmiştir:
async function logFetch(url) {
try {
const response = await fetch(url);
console.log(await response.text());
} catch (err) {
console.log('fetch failed', err);
}
}
Satır sayısı aynıdır ancak tüm geri aramalar kaldırılmıştır. Bu sayede, özellikle de vaatler hakkında daha az bilgi sahibi olanlar için okumak çok daha kolay olur.
Eşzamansız döndürülen değerler
await
kullanıp kullanmadığınıza bakılmaksızın, asynkron işlevler her zaman bir promise döndürür. Bu söz, asynkron işlevin döndürdüğü değerle çözülür veya asynkron işlevin oluşturduğu hatayla reddedilir. 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ı, "world"
ile yerine getirilen bir söz döndürür.
async function foo() {
await wait(500);
throw Error('bar');
}
…foo()
çağrısı, Error('bar')
ile reddedilen bir promise döndürür.
Örnek: Yanıtı akış şeklinde gönderme
Asenkron işlevlerin avantajı, daha karmaşık örneklerde artar. Parçaları günlüğe kaydederken bir yanıtı aktarmak ve nihai boyutu döndürmek istediğinizi varsayalım.
Sözlerimizi yerine getiriyoruz:
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);
});
});
}
Ben Jake "söz veren" Archibald. Asenkron bir döngü oluşturmak için processResult()
işlevini kendi içinde nasıl çağırdığımı görüyor musunuz? Bu yazı beni çok akıllı hissettirdi. Ancak çoğu "akıllı" kodda olduğu gibi, ne yaptığını
anlamak için yaşlarca ona bakmanız gerekiyor.
Bunu tekrar asenkron işlevlerle 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;
}
"Akıllı" olan her şey kayboldu. Kendimi çok iyi hissetmeme neden olan asenkron döngü, güvenilir ve sıkıcı bir while döngüsü ile değiştirildi. şimdi daha iyi oldu. Gelecekte, while
döngüsünü for-of döngüsüyle değiştirerek daha da düzenli hale getirecek asynchronize iteratörlere sahip olacaksınız.
Diğer eş zamansız işlev söz dizimi
async function() {}
'ü daha önce gösterdim ancak async
anahtar kelimesi diğer işlev söz dizimi ile de kullanılabilir:
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 olun. Çok sıralı bir şekilde ilerlemekten kaçının
Senkronize görünen kodlar yazsanız da işlemleri paralel olarak yapma fırsatını kaçırmayın.
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. Buna karşılık:
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!';
}
Yukarıdaki işlemin tamamlanması 500 ms sürer çünkü her iki bekleme de aynı anda gerçekleşir. Pratik bir örneğe göz atalım.
Örnek: Getirme işlemlerini sırayla yayınlama
Bir dizi URL'yi almak ve en kısa sürede doğru sırayla günlüğe kaydetmek istediğinizi varsayalım.
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 zekiyim. Ancak bu kod biraz çok akıllı olduğu için yazmadan daha iyi olacak.
Ancak yukarıdakileri ayarsız bir işleve dönüştürürken çok sıralı bir yaklaşım benimsemek cazip gelebilir:
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ü: jeneratörler
Oluşturucuları destekleyen tarayıcıları (tüm büyük tarayıcıların en son sürümü dahil) hedefliyorsanız asenkron işlevleri bir nevi çoklu doldurabilirsiniz.
Babel bunu sizin için yapar. Babel REPL üzerinden bir örnek
- dönüştürülmüş koda bakarak bu iki kodun ne kadar benzer olduğunu görebilirsiniz. Bu dönüşüm, Babel'in es2017 hazır ayarının bir parçasıdır.
Hedef tarayıcıları senkronize olmayan işlevleri destekledikten sonra devre dışı bırakabileceğiniz için derleme yaklaşımını öneririm. Ancak gerçekten derleyici kullanmak istemiyorsanız Babel'in polyfill'ini alıp kendiniz kullanabilirsiniz. Bunun yerine:
async function slowEcho(val) {
await wait(1000);
return val;
}
…polyfill'i dahil eder ve şunları yazarsınız:
const slowEcho = createAsyncFunction(function* (val) {
yield wait(1000);
return val;
});
createAsyncFunction
için bir oluşturucu (function*
) iletmeniz ve await
yerine yield
kullanmanız gerektiğini unutmayın. Bunun dışında aynı şekilde çalışır.
Geçici çözüm: yenileyici
Eski tarayıcıları hedefliyorsanız Babel, üreteçleri de aktarabilir. Böylece, IE8'e kadar olan tüm tarayıcılarda asenkron işlevleri kullanabilirsiniz. Bunun için Babel'in es2017 hazır ayarına ve es2015 hazır ayarına ihtiyacınız vardır.
Çıkış o kadar güzel değildir. Bu nedenle kod şişmesine dikkat edin.
Her şeyi eş zamansız yapabilirsiniz.
Asenkron işlevler tüm tarayıcılarda kullanıma sunulduğunda, bunları her bir promise döndüren işlevde kullanın. Bunlar, kodunuzu daha düzenli hale getirmekle kalmaz, işlevin her zaman bir promise döndürmesini sağlar.
2014'te asenkron işlevler hakkında çok heyecanlanmıştım ve bunların tarayıcılarda gerçekten kullanıma sunulduğunu görmek harika. Oley!