最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript異步編程之Async/Await用法完全指南

 更新時(shí)間:2026年01月30日 09:54:52   作者:努力進(jìn)步中的小白  
async/await是JavaScript中基于Promise的異步語(yǔ)法糖,使異步代碼更直觀(guān)易讀,這篇文章主要介紹了JavaScript異步編程之Async/Await用法完全指南的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下

一、async/await 的發(fā)展史:為什么會(huì)出現(xiàn)?

要理解 async/await,得先回顧它的 “前輩” 們 ——async/await 不是憑空出現(xiàn)的,而是為了解決 Promise 的 “小痛點(diǎn)”,是 JS 異步編程的終極語(yǔ)法糖。

1. 異步編程的 “進(jìn)化史”(鋪墊)

JS 異步編程的發(fā)展,核心是 “讓異步代碼更像同步代碼”,一步步解決 “寫(xiě)起來(lái)別扭” 的問(wèn)題:

  • 階段 1:回調(diào)函數(shù)(ES5 及以前):解決了異步執(zhí)行的問(wèn)題,但多層嵌套形成 “回調(diào)地獄”,代碼臃腫;
  • 階段 2:Promise(ES2015/ES6,2015 年):解決了回調(diào)地獄,把嵌套改成鏈?zhǔn)秸{(diào)用,但鏈?zhǔn)秸{(diào)用多了(比如 5-6 個(gè)異步操作),then依然會(huì)串聯(lián)成 “長(zhǎng)鏈條”,邏輯不夠直觀(guān);
  • 階段 3:Generator 函數(shù)(ES2015/ES6,過(guò)渡方案):可以暫停 / 恢復(fù)函數(shù)執(zhí)行,能模擬 “同步寫(xiě)異步”,但語(yǔ)法繁瑣(需要手動(dòng)調(diào)用next()),還需要第三方庫(kù)(如 co)封裝,普通人難用;
  • 階段 4:async/await(ES2017/ES8,2017 年):基于 PromiseGenerator,是官方推出的 “語(yǔ)法糖”—— 既保留了 Promise 的異步特性,又能**像寫(xiě)同步代碼一樣寫(xiě)異步邏輯**,成為現(xiàn)在異步編程的主流。

2. 各階段具體代碼示例

階段一:回調(diào)函數(shù)時(shí)代(Callback Hell)

getData(function(data1) {
    process(data1, function(data2) {
        save(data2, function(data3) {
            display(data3, function() {
                // 更多嵌套...
                console.log("完成了,但代碼已經(jīng)看不懂了!");
            });
        });
    });
});

問(wèn)題:代碼橫向發(fā)展,形成"金字塔",難以維護(hù)和調(diào)試。

階段二:Promise時(shí)代(ES6,2015年)

getData()
    .then(process)
    .then(save)
    .then(display)
    .then(() => console.log("完成"))
    .catch(error => console.error("出錯(cuò):", error));

改進(jìn):鏈?zhǔn)秸{(diào)用,縱向發(fā)展,錯(cuò)誤處理統(tǒng)一。

階段三:Generator函數(shù)時(shí)代(過(guò)渡方案)

// 2015年:Generator + Promise
function* asyncGenerator() {
    const data1 = yield getData();
    const data2 = yield process(data1);
    const data3 = yield save(data2);
    yield display(data3);
    console.log("完成");
}

// 需要外部執(zhí)行器
function run(generator) {
    const iterator = generator();
    function handle(result) {
        if (result.done) return;
        result.value.then(data => {
            handle(iterator.next(data));
        });
    }
    handle(iterator.next());
}

問(wèn)題:需要外部執(zhí)行器,語(yǔ)法復(fù)雜,不直觀(guān)。

階段四:Async/Await時(shí)代(ES8,2017年)

// 2017年:Async/Await
async function handleData() {
    try {
        const data1 = await getData();
        const data2 = await process(data1);
        const data3 = await save(data2);
        await display(data3);
        console.log("完成");
    } catch (error) {
        console.error("出錯(cuò):", error);
    }
}

革命性改進(jìn):代碼看起來(lái)像同步代碼,但實(shí)際上是異步執(zhí)行!

3. 核心本質(zhì)

async/await 不是替代 Promise,而是基于 Promise 的語(yǔ)法糖—— 所有 async/await 能實(shí)現(xiàn)的功能,用 Promise 都能實(shí)現(xiàn),只是 async/await 寫(xiě)起來(lái)更簡(jiǎn)單、更易讀。

二、async/await 的核心用法

1. 基本語(yǔ)法:async + await

(1)async:聲明 “異步函數(shù)”

  • async修飾的函數(shù),會(huì)變成 “異步函數(shù)”;
  • 異步函數(shù)的返回值會(huì)自動(dòng)包裝成 Promise(哪怕你返回的是普通值)。
// 在函數(shù)前加上 async,這個(gè)函數(shù)就變成了異步函數(shù)
async function normalFunction() {
    return "Hello Async";  // 自動(dòng)包裝成Promise
}

// 等價(jià)于:
function normalFunction() {
    return Promise.resolve("Hello Async");
}

// 使用
normalFunction().then(console.log);  // "Hello Async"

(2)await:等待 Promise 完成

  • await只能用在async 函數(shù)內(nèi)部(這是新手最容易踩的坑);
  • await后面必須跟一個(gè)Promise 對(duì)象(如果不是,會(huì)直接返回該值);
  • await會(huì) “暫停” 函數(shù)執(zhí)行,直到 Promise 完成(成功 / 失敗),再繼續(xù)執(zhí)行后面的代碼 —— 但這個(gè) “暫停” 是非阻塞的(不會(huì)卡主線(xiàn)程)。
async function fetchUser() {
    // await 會(huì)暫停函數(shù)執(zhí)行,等待Promise完成
    const response = await fetch('https://api.example.com/user');
    const user = await response.json();
    return user;
}

// await 只能在 async 函數(shù)內(nèi)部使用
// 下面的代碼會(huì)報(bào)錯(cuò):
// const data = await fetchUser();  // ? 錯(cuò)誤

?? 對(duì)比 Promise:原來(lái)的.then鏈?zhǔn)秸{(diào)用,現(xiàn)在直接用await拿到結(jié)果,代碼和同步邏輯完全一致!

(3)基本用法示例

// 模擬異步函數(shù)
function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function example() {
    console.log("開(kāi)始");
    
    await delay(1000);  // 等待1秒
    console.log("1秒后");
    
    await delay(2000);  // 再等待2秒
    console.log("再過(guò)2秒后");
    
    return "任務(wù)完成";
}

// 調(diào)用
example().then(result => console.log(result));
// 輸出:
// 開(kāi)始
// 1秒后
// 再過(guò)2秒后
// 任務(wù)完成

2. 錯(cuò)誤處理:try/catch(核心)

Promise 用.catch處理錯(cuò)誤,async/await 用try/catch捕獲錯(cuò)誤(因?yàn)?await 會(huì) “解包” Promise 的失敗狀態(tài),必須手動(dòng)捕獲)。

示例 1:捕獲單個(gè)異步操作的錯(cuò)誤

async function getScoreAsync() {
  try {
    // 成功:執(zhí)行try里的代碼,失?。禾D(zhuǎn)到catch
    const score = await getScore(); 
    console.log("分?jǐn)?shù):", score);
  } catch (error) {
    // 捕獲Promise的reject錯(cuò)誤
    console.log("出錯(cuò)了:", error);
  }
}

// 測(cè)試:如果getScore返回reject(比如分?jǐn)?shù)50),會(huì)執(zhí)行catch
getScoreAsync();

示例 2:捕獲多個(gè)異步操作的錯(cuò)誤

如果有多個(gè) await一個(gè) try/catch 可以捕獲所有錯(cuò)誤:

// 模擬兩個(gè)異步請(qǐng)求
function getUser() {
  return new Promise((resolve) => setTimeout(() => resolve({ id: 1 }), 500));
}
function getCourse(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      userId === 1 ? resolve({ name: "語(yǔ)文" }) : reject("用戶(hù)不存在");
    }, 500);
  });
}

// 用async/await調(diào)用,統(tǒng)一捕獲錯(cuò)誤
async function getInfo() {
  try {
    const user = await getUser(); // 第一個(gè)異步操作
    const course = await getCourse(user.id); // 第二個(gè)異步操作
    console.log("用戶(hù)課程:", course); // 輸出:{ name: "語(yǔ)文" }
  } catch (error) {
    console.log("錯(cuò)誤:", error); // 任何一個(gè)異步失敗,都會(huì)走到這里
  }
}

getInfo();

3. 實(shí)戰(zhàn)案例:替代 Promise 鏈?zhǔn)秸{(diào)用(解決回調(diào)地獄)

回顧之前 “登錄→查課程→查成績(jī)” 的案例,用 async/await 改寫(xiě)后,代碼更簡(jiǎn)潔:

// 封裝3個(gè)異步請(qǐng)求(返回Promise)
function login() {
  return new Promise(resolve => setTimeout(() => resolve({ id: 1 }), 500));
}
function getCourse(userId) {
  return new Promise(resolve => setTimeout(() => resolve({ id: 10 }), 500));
}
function getScore(courseId) {
  return new Promise(resolve => setTimeout(() => resolve({ score: 90 }), 500));
}

// async/await版本:同步寫(xiě)法,異步執(zhí)行
async function getStudentInfo() {
  try {
    const user = await login(); // 登錄
    const course = await getCourse(user.id); // 查課程
    const score = await getScore(course.id); // 查成績(jī)
    console.log("最終成績(jī):", score); // 輸出:{ score: 90 }
  } catch (error) {
    console.log("出錯(cuò)了:", error);
  }
}

getStudentInfo();

對(duì)比 Promise 鏈?zhǔn)秸{(diào)用:沒(méi)有任何.then,代碼和同步邏輯完全一致,新手也能一眼看懂執(zhí)行順序。

4. 進(jìn)階用法:并發(fā)執(zhí)行(避免不必要的等待)

新手容易犯的錯(cuò):用 await 串行執(zhí)行所有異步操作,導(dǎo)致耗時(shí)過(guò)長(zhǎng)。比如:

// 錯(cuò)誤示例:串行執(zhí)行(總耗時(shí)=1s+1s=2s)
async function badDemo() {
  const res1 = await new Promise(resolve => setTimeout(() => resolve(1), 1000));
  const res2 = await new Promise(resolve => setTimeout(() => resolve(2), 1000));
  console.log(res1, res2); //  // 總時(shí)間 = 2個(gè)請(qǐng)求時(shí)間之和=2s
}

如果兩個(gè)異步操作互不依賴(lài)(比如同時(shí)查兩個(gè)獨(dú)立的接口),應(yīng)該用Promise.all實(shí)現(xiàn)并發(fā):

// 正確示例:并發(fā)執(zhí)行(總耗時(shí)=1s)
async function goodDemo() {
  // 1. 先創(chuàng)建兩個(gè)Promise對(duì)象(不要先await)
  const promise1 = new Promise(resolve => setTimeout(() => resolve(1), 1000));
  const promise2 = new Promise(resolve => setTimeout(() => resolve(2), 1000));
  
  // 2. 用Promise.all并發(fā)執(zhí)行,await等待所有完成
  const [res1, res2] = await Promise.all([promise1, promise2]);
  console.log(res1, res2); // 總耗時(shí)1秒
}

?? 核心原則:

  • 異步操作有依賴(lài)(比如查成績(jī)依賴(lài)課程 ID):用 await 串行執(zhí)行;
  • 異步操作無(wú)依賴(lài):用Promise.all+await 并發(fā)執(zhí)行,提升效率。

5. 循環(huán)中的 await:避免踩坑

如果在循環(huán)中用 await,默認(rèn)是串行執(zhí)行,比如:

// 串行執(zhí)行:總耗時(shí)3秒(1+1+1)
async function loopDemo() {
  const list = [1, 2, 3];
  for (const item of list) {
    await new Promise(resolve => setTimeout(() => {
      console.log(item);
      resolve();
    }, 1000));
  }
}

注意forEach 中的 async/await 不會(huì)按預(yù)期工作(? 錯(cuò)誤)

如果想讓循環(huán)中的異步操作并發(fā)執(zhí)行,先收集所有 Promise,再用Promise.all

//  map + Promise.all(并行) 并發(fā)執(zhí)行:總耗時(shí)1秒
async function loopDemo() {
  const list = [1, 2, 3];
  // 1. 收集所有Promise
  const promises = list.map(item => 
    new Promise(resolve => setTimeout(() => {
      console.log(item);
      resolve();
    }, 1000))
  );
  // 2. 并發(fā)執(zhí)行
  await Promise.all(promises);
}

三、async/await 的常見(jiàn)坑與注意事項(xiàng)

  1. await 只能在 async 函數(shù)中使用:如果在普通函數(shù)里用 await,會(huì)直接報(bào)錯(cuò)(比如在全局作用域、普通 for 循環(huán)回調(diào)里);
    ? 解決:要么把函數(shù)改成 async,要么用 IIFE(立即執(zhí)行函數(shù))包裹:
// 全局作用域使用await(ES模塊環(huán)境)
(async () => {
  const score = await getScore();
  console.log(score);
})();
  1. 不要濫用 await:所有異步操作都用 await 串行執(zhí)行,會(huì)導(dǎo)致性能極低(比如 10 個(gè)獨(dú)立接口,本可以 1 秒完成,結(jié)果用了 10 秒);
    ? 解決無(wú)依賴(lài)的異步操作,用Promise.all并發(fā)執(zhí)行。

  2. 錯(cuò)誤捕獲要完整:如果漏寫(xiě) try/catch,await 的 Promise 失敗時(shí),會(huì)拋出 “未捕獲的錯(cuò)誤”,導(dǎo)致程序崩潰;
    ? 解決:要么用 try/catch,要么在 await 后面加.catch:

    // 單個(gè)異步操作的簡(jiǎn)化錯(cuò)誤捕獲
    const score = await getScore().catch(error => console.log(error));
    

四、總結(jié):核心要點(diǎn)回顧

發(fā)展史:async/await 是 ES2017(2017)納入標(biāo)準(zhǔn)的語(yǔ)法糖,基于 Promise,解決了 Promise 鏈?zhǔn)秸{(diào)用的繁瑣,讓異步代碼像同步代碼;

核心語(yǔ)法:

  • async修飾函數(shù),使其返回 Promise;
  • await只能在 async 函數(shù)內(nèi)使用,等待 Promise 完成并返回結(jié)果;
  • 錯(cuò)誤處理用try/catch(替代 Promise 的.catch)。

使用原則:

  • 有依賴(lài)的異步操作:await 串行執(zhí)行;
  • 無(wú)依賴(lài)的異步操作:Promise.all+await 并發(fā)執(zhí)行;
  • 必加錯(cuò)誤捕獲,避免程序崩潰。

async/await 是目前 JS 異步編程的 “最優(yōu)解”,掌握它后,不管是前端調(diào)接口、Node.js 寫(xiě)后端,處理異步邏輯都會(huì)變得極其簡(jiǎn)單~

到此這篇關(guān)于JavaScript異步編程之Async/Await用法的文章就介紹到這了,更多相關(guān)JS Async/Await用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

天津市| 德州市| 新和县| 漳浦县| 静乐县| 克拉玛依市| 马关县| 施秉县| 城步| 镇沅| 济宁市| 青神县| 永修县| 黄石市| 昌宁县| 昔阳县| 卫辉市| 云霄县| 全南县| 洞头县| 康平县| 肃南| 修文县| 商洛市| 荃湾区| 盐池县| 包头市| 湖口县| 唐山市| 佛冈县| 高密市| 徐水县| 来宾市| 光山县| 广西| 前郭尔| 嘉兴市| 秀山| 常州市| 奇台县| 浦东新区|