非同步函式可讓您編寫以承諾為基準的程式碼,就像同步作業一樣。
根據預設,Chrome、Edge、Firefox 和 Safari 都會啟用非同步功能,以及 他們真是太不可思議了可讓你編寫承諾使用的程式碼 但是沒有封鎖主執行緒的話。會把 較簡單的非同步程式碼更容易閱讀
非同步函式的運作方式如下:
async function myFirstAsyncFunction() {
try {
const fulfilledValue = await promise;
} catch (rejectedValue) {
// …
}
}
如果在函式定義之前使用 async
關鍵字,接著可以使用
函式中的 await
。當您 await
承諾時,函式就會暫停
並以無阻塞的方式,直到達成承諾如果保證符合
才能取得價值如果承諾產品遭拒,系統就會擲回遭拒的值。
瀏覽器支援
範例:記錄擷取作業
假設您想擷取網址並將回應記錄為文字。以下是顯示畫面 承諾使用以下承諾:
function logFetch(url) {
return fetch(url)
.then((response) => response.text())
.then((text) => {
console.log(text);
})
.catch((err) => {
console.error('fetch failed', err);
});
}
以下是使用非同步函式的做法:
async function logFetch(url) {
try {
const response = await fetch(url);
console.log(await response.text());
} catch (err) {
console.log('fetch failed', err);
}
}
行數相同,但所有回呼都消失。如此一來 更容易閱讀,對於不熟悉承諾的人而言更是如此。
非同步傳回值
無論您是否使用 await
,非同步函式都「一律」傳回承諾值。沒錯
保證會解析非同步函式傳回的內容,或拒絕含有
任何非同步函式擲回的內容因此,您可以使用:
// wait ms milliseconds
function wait(ms) {
return new Promise((r) => setTimeout(r, ms));
}
async function hello() {
await wait(500);
return 'world';
}
...呼叫 hello()
會傳回承諾,具有 "world"
的「執行要求」。
async function foo() {
await wait(500);
throw Error('bar');
}
...呼叫 foo()
會傳回使用 Error('bar')
拒絕的承諾。
範例:串流回應
在更複雜的範例中,非同步函式的優點會增加。假設您想要 在登出區塊時串流回應,然後傳回最終大小。
以下為承諾:
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);
});
});
}
看看我,Jake「Welder of Promiss」「封存」。查看通話方式
是否內部 processResult()
來設定非同步迴圈?寫作創作
我覺得非常聰明。但就像最「聰明」的您必須全心投入
但孩子們會想一探究竟
90 年代
請使用非同步函式再試一次:
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;
}
所有「聰明」消失。這個非同步迴圈讓我覺得很自在
並替換成可靠、無趣的表情。這樣好多了。在未來
非同步疊代器
會
使用 For-of-of 迴圈取代 while
迴圈,使其更臻完善。
其他非同步函式語法
我已經顯示async function() {}
了,但async
關鍵字可以
與其他函式語法搭配使用:
箭頭函式
// map some URLs to json-promises
const jsonPromises = urls.map(async (url) => {
const response = await fetch(url);
return response.json();
});
物件方法
const storage = {
async getAvatar(name) {
const cache = await caches.open('avatars');
return cache.match(`/avatars/${name}.jpg`);
}
};
storage.getAvatar('jaffathecake').then(…);
類別方法
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(…);
請留意!避免過於循序漸進
雖然您編寫的程式碼為同步性質,但請確保您不會遺漏 能夠同時執行各種操作
async function series() {
await wait(500); // Wait 500ms…
await wait(500); // …then wait another 500ms.
return 'done!';
}
上述作業需要 1000 毫秒才能完成,但:
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!';
}
上述需要 500 毫秒才能完成,因為這兩個等待事件會同時發生。 我們來看看一個實際範例。
範例:輸出擷取順序
假設您想擷取一系列網址並盡快記錄, 正確順序。
深呼吸 - 外觀如下:
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());
}
沒錯,我使用 reduce
鏈結一系列承諾。我所以
智慧型功能。不過,這還有一點聰明的編碼,沒關係。
但是,在將上述函式轉換為非同步函式時,您可能會很想 依序:
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); } }
瀏覽器支援的替代方案:產生器
如果您指定的瀏覽器支援產生器 (包括 其實是最新版 ),您可以排序 polyfill async 函式。
Babel 會為您代勞, 請觀看 Babel REPL 的範例
- 也要注意轉譯程式碼的相似度這項轉換已納入 Babel 的 es2017 預設設定。
我建議你把作業改成 目標瀏覽器支援非同步函式,但如果您「確實」不想使用 可將程式碼轉譯為解碼器 Babel 的 polyfill 並親自使用而不是這樣
async function slowEcho(val) {
await wait(1000);
return val;
}
您要加入 polyfill 然後寫入:
const slowEcho = createAsyncFunction(function* (val) {
yield wait(1000);
return val;
});
請注意,您必須將產生器 (function*
) 傳遞至 createAsyncFunction
,
並使用 yield
取代 await
。除此之外,使用方法也大同小異。
解決方法:重新產生器
如果您指定舊版瀏覽器,Babel 也可以採用轉譯器的轉譯器 可讓您從 IE8 至今使用非同步函式。如要這麼做, Babel 的 es2017 預設 「和」es2015 預設設定。
輸出內容不如美化,因此請留意 大量程式碼
同步處理所有內容!
所有瀏覽器都導入非同步函式後, 承諾傳回函式!不但讓程式碼更簡潔 請確保函式「一律」傳回承諾值。
我很期待收到非同步函式 2014 年, 能用瀏覽器看待他們真的會很開心太好了!