Eş zamansız işlevler, sanki eşzamanlı olan vaadi tabanlı kodlar yazmanıza imkan tanır.
Chrome, Edge, Firefox ve Safari'de eş zamansız işlevler varsayılan olarak etkindir ve açıkçası oldukça harikadır. Eşzamanlıymış gibi, ancak ana iş parçacığını engellemeden tarihe dayalı kod yazmanıza imkan tanırlar. Bunlar, eşzamansız kodunuzu daha az "zeki" ve daha okunabilir hale getirir.
Eş zamansız işlevler şu şekilde çalışır:
async function myFirstAsyncFunction() {
try {
const fulfilledValue = await promise;
} catch (rejectedValue) {
// …
}
}
async
anahtar kelimesini bir işlev tanımından önce kullanırsanız işlev içinde await
kullanabilirsiniz. Bir sözü await
verdiğinizde söz konusu olana kadar işlev engelleme olmadan duraklatılır. Vaadiniz karşılanırsa
değeri geri alırsınız. Vaat reddedilirse, reddedilen değer reddedilir.
Tarayıcı desteği
Örnek: Bir getirme işlemini günlüğe kaydetme
Bir URL getirmek ve yanıtı metin olarak günlüğe kaydetmek istediğinizi düşünelim. Vaatler kullanıldığında şu şekilde görünür:
function logFetch(url) {
return fetch(url)
.then((response) => response.text())
.then((text) => {
console.log(text);
})
.catch((err) => {
console.error('fetch failed', err);
});
}
Eş zamansız fonksiyonların kullanımında da aynı durum söz konusudur:
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 kullanır, ancak tüm geri çağırma işlemleri iptal edilir. Bu da, özellikle vaatler konusuna aşina olmayanların okumasını kolaylaştırır.
Eş zamansız dönüş değerleri
await
uygulamasını kullansanız da kullanmasanız da eş zamansız işlevler her zaman bir söz döndürür. Bu söz, eşzamansız işlevin döndürdüğü her şeyle çözümlenir veya eşzamansız işlevin döndürdüğü şeyle reddedilir. Yani şunun için:
// 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 karşılayan bir söz döndürür.
async function foo() {
await wait(500);
throw Error('bar');
}
...foo()
çağrısı, Error('bar')
ile reddeden bir söz döndürür.
Örnek: yanıt akışı
Karmaşık örneklerde eşzamansız işlevlerin avantajı daha fazladır. Parçalardan çıkış yaparken bir yanıtı akış olarak yayınlamak ve son boyutu döndürmek istediğinizi varsayalım.
İşte vaatler:
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);
});
});
}
Bana göz atın, Jake "vaatlerin sahibi" Archibald. Eşzamansız bir döngü oluşturmak için kendi içinde
processResult()
'i nasıl çağırdığımı görüyor musunuz? Çok akıllı hissettiren yazılar. Ancak çoğu "akıllı" kodda olduğu gibi, ne yaptığını
anlamak için ona yıllarca bakmanız gerekiyor. Örneğin, 90'lardaki sihirli göz resimlerden biri.
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ı" özellikler geride kaldı. Kendimi beğenmiş hissetmemi sağlayan eşzamansız döngü
yerine güvenilir, sıkıcı, sıra dışı bir döngü geldi. şimdi daha iyi oldu. Gelecekte, while
döngüsünü bir for-of döngü ile değiştirecek ve daha da düzenli hale getirecek eşzamansız yinelemeler edineceksiniz.
Diğer eşzamansız işlev söz dizimi
Size async function() {}
bağlantısını zaten gösterdik ancak async
anahtar kelimesi başka işlev söz dizimiyle 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 ol! Çok sıralı bir geçişten kaçının
Eşzamanlı görünen kod yazıyor olsanız da işlemleri paralel olarak yapma fırsatını kaçırmadığınızdan emin olun.
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. Bununla birlikte:
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 aynı anda gerçekleştiğinden yukarıdaki işlemin tamamlanması 500 ms sürer. Şimdi bir örnek inceleyelim.
Örnek: Getirmelerin çıktısını sırayla alma
Bir dizi URL'yi getirmek ve bunları mümkün olan en kısa sürede doğru sırada günlüğe kaydetmek istediğinizi varsayalım.
Derin nefes: İşte vadedilenler:
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());
}
Evet, doğru. Bir dizi vaatte bulunmak için reduce
kullanıyorum. Çok akıllıyım. Ancak bu, kod kullanmadan daha iyi sonuç alabileceğiniz akıllı bir kodlamadır.
Ancak, yukarıdakileri eşzamansız bir işleve dönüştürürken çok sıralı seçeneği tercih edebilirsiniz:
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şturucuları destekleyen tarayıcıları (her başlıca tarayıcının en son sürümünü içerir) hedefliyorsanız eş zamansız çoklu dolgu işlevlerini sıralayabilirsiniz.
Babel bunu sizin yerinize yapacaktır. Babel REPL'den alınan bir örneği burada bulabilirsiniz.
- koda ne kadar benzer olduğuna dikkat edin. Bu dönüşüm Babel es2017 hazır ayarının bir parçasıdır.
Aktarma yaklaşımını kullanmanızı öneririm, çünkü hedef tarayıcılarınız eş zamansız işlevleri desteklediğinde bunu kapatabilirsiniz, ancak gerçekten bir aktarıcı kullanmak istemiyorsanız Babel'in çoklu dolgusunu alıp kendiniz kullanabilirsiniz. Şunun yerine:
async function slowEcho(val) {
await wait(1000);
return val;
}
Çoklu dolguyu eklemeniz ve şunu yazmanız gerekir:
const slowEcho = createAsyncFunction(function* (val) {
yield wait(1000);
return val;
});
createAsyncFunction
hizmetine bir oluşturucu (function*
) iletmeniz ve await
yerine yield
kullanmanız gerektiğini unutmayın. Bunların dışında aynı şekilde çalışır.
Geçici çözüm: regenerator
Daha eski tarayıcıları hedefliyorsanız Babel, oluşturucuları da aktarabilirsiniz. Böylece, eş zamansız işlevleri IE8'e kadar kullanabilirsiniz. Bunun için Babel es2017 hazır ayarına ve es2015 hazır ayarına ihtiyacınız vardır.
Çıkış çok hoş değildir, bu nedenle kod şişmesine dikkat edin.
Eş zamansız her şey!
Eş zamansız işlevler tüm tarayıcılara ulaştığında, bunları tüm vaat eden işlevlerde kullanın. Hem kodunuzu daha düzenli hale getirir hem de işlevin her zaman bir vaat getirmesini sağlar.
2014 yılında eşzamansız işlevler konusunda gerçekten çok heyecanlandım ve bunların gerçek olarak tarayıcılara ulaştığını görmek çok güzel. Tüh!