JavaScript中Async/Await通過(guò)同步的方式實(shí)現(xiàn)異步的方法介紹
一、異步編程的問(wèn)題
在 Web 開發(fā)中,我們經(jīng)常需要進(jìn)行異步操作,比如從服務(wù)器獲取數(shù)據(jù),或者執(zhí)行耗時(shí)操作。這些任務(wù)通常需要一定的時(shí)間來(lái)完成,而在這段時(shí)間內(nèi),JavaScript 不能停止執(zhí)行其他代碼,否則會(huì)導(dǎo)致界面卡住或者無(wú)響應(yīng)。因此,我們需要使用異步編程技術(shù)來(lái)處理這些操作。
傳統(tǒng)的異步編程技術(shù)有回調(diào)函數(shù)和 Promise。使用回調(diào)函數(shù)時(shí),我們需要將后續(xù)的操作寫在回調(diào)函數(shù)中,這樣才能確保在異步操作完成后執(zhí)行。回調(diào)函數(shù)雖然簡(jiǎn)單易用,但是嵌套多個(gè)回調(diào)函數(shù)會(huì)導(dǎo)致代碼難以閱讀和維護(hù)。而 Promise 解決了這個(gè)問(wèn)題,它可以鏈?zhǔn)秸{(diào)用,避免了回調(diào)函數(shù)嵌套的問(wèn)題。但是,Promise 的語(yǔ)法并不是那么直觀,需要一定的學(xué)習(xí)成本。
為了更加直觀地表達(dá)異步代碼,ES2017 引入了 Async/Await。Async/Await 可以使異步代碼看起來(lái)像同步代碼,這樣可以使代碼更加易于理解和維護(hù)。接下來(lái),我們將詳細(xì)講解 Async/Await 的實(shí)現(xiàn)原理。
二、 Async/Await 的實(shí)現(xiàn)原理
Async 和 Await 都是異步編程的關(guān)鍵字。在 ES2017 中,Async 函數(shù)用來(lái)聲明一個(gè)異步函數(shù),它的定義方式類似于普通的函數(shù),但是在函數(shù)關(guān)鍵字前面添加 async 關(guān)鍵字,如下所示:
async function fetchData() {
// 異步操作
}我們可以在 Async 函數(shù)內(nèi)部使用 await 關(guān)鍵字來(lái)等待異步操作完成。await 表示等待異步操作返回結(jié)果后再繼續(xù)執(zhí)行后續(xù)的代碼。
async function fetchData() {
const result = await fetch("/api/data");
console.log(result);
}這段代碼中,我們使用了 fetch 函數(shù)來(lái)獲取服務(wù)器數(shù)據(jù),fetch 返回的是 Promise 實(shí)例。我們?cè)谠?Promise 實(shí)例前面加上 await 關(guān)鍵字,表示等待該 Promise 對(duì)象返回?cái)?shù)據(jù)后再繼續(xù)執(zhí)行后續(xù)的代碼。
當(dāng) Async 函數(shù)被調(diào)用時(shí),它返回的是一個(gè) Promise 對(duì)象。Promise 對(duì)象有三種狀態(tài):已完成、已拒絕和未完成。如果 Async 函數(shù)內(nèi)部沒有拋出異常,則該 Promise 對(duì)象將進(jìn)入已完成狀態(tài),并返回 Async 函數(shù)返回值;如果 Async 函數(shù)內(nèi)部拋出異常,則該 Promise 對(duì)象將進(jìn)入已拒絕狀態(tài),并返回拋出的異常。例如,下面這個(gè)例子中,Async 函數(shù)返回的 Promise 對(duì)象的狀態(tài)為已完成,并返回字符串 "Hello World!":
async function hello() {
return "Hello World!";
}
hello().then((result) => console.log(result)); // 輸出 "Hello World!"在下面的示例中,我們使用 Async/Await 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的異步操作:
async function fetchData() {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/posts");
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
fetchData();在這個(gè)例子中,我們使用了 fetch 函數(shù)來(lái)獲取服務(wù)器數(shù)據(jù),并且使用 await 等待數(shù)據(jù)返回。如果出現(xiàn)異常,則使用 try...catch 處理異常。這段代碼使用起來(lái)非常直觀和易于理解。
Async/Await 的同步實(shí)現(xiàn)原理
雖然使用 Async/Await 可以使異步代碼看起來(lái)像同步代碼,但是底層仍然是異步執(zhí)行的。那么,Async/Await 是如何通過(guò)同步的方式實(shí)現(xiàn)異步的呢?答案就是 Generator 函數(shù)和 Promise。
Generator 函數(shù)是一種特殊的函數(shù),它可以被暫停和恢復(fù)執(zhí)行。在 Generator 函數(shù)中,我們可以使用 yield 關(guān)鍵字將控制權(quán)交給調(diào)用方,并在下次調(diào)用時(shí)從上次暫停的位置繼續(xù)執(zhí)行。這種特性可以用來(lái)實(shí)現(xiàn)異步操作。
Promise 是 ES6 引入的另一種異步編程技術(shù)。Promise 對(duì)象表示一個(gè)尚未完成或失敗的操作,它可以被異步執(zhí)行,并返回一個(gè)代表操作結(jié)果的值。
Async 函數(shù)實(shí)際上是一種特殊的 Generator 函數(shù),它使用 yield 關(guān)鍵字暫停執(zhí)行,并在異步操作完成后,通過(guò)調(diào)用 next 方法恢復(fù)執(zhí)行。這個(gè)過(guò)程中,Async 函數(shù)內(nèi)部創(chuàng)建了一個(gè) Promise 對(duì)象,并將該 Promise 對(duì)象返回給調(diào)用方。下面是 Async 函數(shù)的簡(jiǎn)化版實(shí)現(xiàn):
function asyncToGenerator(generatorFunc) {
return function () {
const generator = generatorFunc.apply(this, arguments);
return new Promise((resolve, reject) => {
function step(key, arg) {
let generatorResult;
try {
generatorResult = generator[key](arg);
} catch (error) {
reject(error);
}
const { value, done } = generatorResult;
if (done) {
resolve(value);
} else {
Promise.resolve(value).then(
(result) => step("next", result),
(error) => step("throw", error)
);
}
}
step("next");
});
};
}這段代碼中,我們定義了一個(gè)名為 asyncToGenerator 的函數(shù),它接收一個(gè) Generator 函數(shù)作為參數(shù),并返回一個(gè) Promise 對(duì)象。在 asyncToGenerator 函數(shù)內(nèi)部,我們首先調(diào)用傳入的 Generator 函數(shù),獲取到一個(gè)迭代器對(duì)象。然后,我們?cè)?Promise 對(duì)象的構(gòu)造函數(shù)中使用遞歸調(diào)用的方式來(lái)處理每一次迭代。如果當(dāng)前迭代已經(jīng)完成,則調(diào)用 resolve 函數(shù),將結(jié)果返回給調(diào)用方;否則,將該迭代的 Promise 對(duì)象通過(guò) then 方法注冊(cè)成功和失敗回調(diào)函數(shù),并在回調(diào)函數(shù)中繼續(xù)處理下一次迭代。
三、Async/Await 的使用場(chǎng)景
Async/Await 通常用于處理多個(gè)異步操作的情況,它可以避免回調(diào)地獄和 Promise 層層嵌套的問(wèn)題。下面是一個(gè)具有挑戰(zhàn)性的使用場(chǎng)景。
假設(shè)我們需要獲取某些商品的信息并計(jì)算它們的總價(jià)格,其中每個(gè)商品需要從服務(wù)器獲取,并且需要等待前一個(gè)商品請(qǐng)求完成后才能發(fā)送下一次請(qǐng)求。在寫傳統(tǒng)異步代碼時(shí),我們可能會(huì)陷入回調(diào)地獄:
function getTotalPrice(items) {
let totalPrice = 0;
fetchItem(0, items);
function fetchItem(index, items) {
if (index >= items.length) {
console.log("totalPrice:", totalPrice);
return;
}
const item = items[index];
fetch(`/api/items/${item.id}`).then((response) => {
response.json().then((data) => {
item.price = data.price;
totalPrice += item.price * item.count;
fetchItem(index + 1, items);
});
});
}
}這段代碼中,我們首先定義了一個(gè) getTotalPrice 函數(shù),它接收一個(gè)商品列表作為參數(shù),并計(jì)算所有商品的總價(jià)格。我們?cè)谠摵瘮?shù)中定義了一個(gè)名為 fetchItem 的遞歸函數(shù),用于依次獲取每個(gè)商品的價(jià)格,并累加到 totalPrice 變量中。在 fetchItem 函數(shù)中,我們使用 fetch 函數(shù)獲取商品信息,然后使用嵌套的 Promise.then 調(diào)用來(lái)等待異步操作返回。這段代碼雖然可行,但是非常難以理解和維護(hù)。
使用 Async/Await 可以讓代碼更加直觀和易于理解:
async function getTotalPrice(items) {
let totalPrice = 0;
for (let item of items) {
const response = await fetch(`/api/items/${item.id}`);
const data = await response.json();
item.price = data.price;
totalPrice += item.price * item.count;
}
console.log("totalPrice:", totalPrice);
}可以看到,在上面的代碼中,我們使用了 Async/Await 和 for...of 循環(huán),避免了回調(diào)地獄和 Promise 層層嵌套的問(wèn)題。這樣的代碼看起來(lái)非常簡(jiǎn)單和直觀。
四、小結(jié)一下
Async/Await 是一種比較新的異步編程技術(shù),它使異步代碼看起來(lái)像同步代碼,更加直觀和易于理解。Async/Await 的實(shí)現(xiàn)原理是 Generator 函數(shù)和 Promise,它通過(guò)同步的方式實(shí)現(xiàn)異步。使用 Async/Await 可以避免回調(diào)地獄和 Promise 層層嵌套的問(wèn)題。Async/Await 通常用于處理多個(gè)異步操作的情況,這樣的代碼看起來(lái)非常簡(jiǎn)單和直觀。
以上就是JavaScript中Async/Await通過(guò)同步的方式實(shí)現(xiàn)異步的方法介紹的詳細(xì)內(nèi)容,更多關(guān)于JavaScript Async/Await異步的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
小程序?qū)崿F(xiàn)搜索界面 小程序?qū)崿F(xiàn)推薦搜索列表效果
這篇文章主要為大家詳細(xì)介紹了小程序?qū)崿F(xiàn)搜索界面,小程序?qū)崿F(xiàn)推薦搜索列表效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05
JavaScript+Html5實(shí)現(xiàn)按鈕復(fù)制文字到剪切板功能(手機(jī)網(wǎng)頁(yè)兼容)
在學(xué)習(xí)javascript的過(guò)程中,遇到一個(gè)問(wèn)題就是基于JavaScript+Html5實(shí)現(xiàn)按鈕復(fù)制文字到剪切板功能,下面小編給大家分享下我的實(shí)現(xiàn)思路,感興趣的朋友可以參考下2017-03-03
javascript過(guò)濾數(shù)組重復(fù)元素的實(shí)現(xiàn)方法
這篇文章主要介紹了javascript過(guò)濾數(shù)組重復(fù)元素的實(shí)現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下2017-05-05
javaScript如何處理從java后臺(tái)返回的list
把List集合轉(zhuǎn)成json格式傳到界面上,這樣js里面操作也簡(jiǎn)單,具體的解決方法如下,大家可以參考學(xué)習(xí)下2014-04-04
深入了解JavaScript中遞歸的理解與實(shí)現(xiàn)
本文將通過(guò)遞歸的經(jīng)典案例:求斐波那契數(shù)來(lái)講解遞歸,通過(guò)畫遞歸樹的方式來(lái)講解其時(shí)間復(fù)雜度和空間復(fù)雜度以及遞歸的執(zhí)行順序,感興趣的可以了解一下2022-06-06
微信小程序BindTap快速連續(xù)點(diǎn)擊目標(biāo)頁(yè)面跳轉(zhuǎn)多次問(wèn)題處理
這篇文章主要介紹了微信小程序BindTap快速連續(xù)點(diǎn)擊目標(biāo)頁(yè)面跳轉(zhuǎn)多次問(wèn)題處理,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-04-04
setTimeout內(nèi)不支持jquery的選擇器的解決方案
在JS中無(wú)論是setTimeout還是setInterval,在使用函數(shù)名作為調(diào)用句柄時(shí)都不能帶參數(shù),而在許多場(chǎng)合必須要帶參數(shù),這就需要想方法解決。2015-04-04
使用濾鏡設(shè)置透明導(dǎo)致 IE 6/7/8/9 解析異常的解決方法
使用濾鏡設(shè)置透明導(dǎo)致 IE 6/7/8/9 解析異常的解決方法,需要的朋友可以參考下。2011-04-04
JS實(shí)現(xiàn)自動(dòng)輪播圖效果(自適應(yīng)屏幕寬度+手機(jī)觸屏滑動(dòng))
這篇文章主要介紹了JS實(shí)現(xiàn)自動(dòng)輪播圖效果(自適應(yīng)屏幕寬度+手機(jī)觸屏滑動(dòng)),需要的朋友可以參考下2017-06-06

