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

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

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

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

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

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

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

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

問題:代碼橫向發(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í)代(過渡方案)

// 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());
}

問題:需要外部執(zhí)行器,語法復(fù)雜,不直觀。

階段四: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):代碼看起來像同步代碼,但實(shí)際上是異步執(zhí)行!

3. 核心本質(zhì)

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

二、async/await 的核心用法

1. 基本語法: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ì)卡主線程)。
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:原來的.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("開始");
    
    await delay(1000);  // 等待1秒
    console.log("1秒后");
    
    await delay(2000);  // 再等待2秒
    console.log("再過2秒后");
    
    return "任務(wù)完成";
}

// 調(diào)用
example().then(result => console.log(result));
// 輸出:
// 開始
// 1秒后
// 再過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里的代碼,失敗:跳轉(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: "語文" }) : reject("用戶不存在");
    }, 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("用戶課程:", course); // 輸出:{ name: "語文" }
  } catch (error) {
    console.log("錯(cuò)誤:", error); // 任何一個(gè)異步失敗,都會(huì)走到這里
  }
}

getInfo();

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

回顧之前 “登錄→查課程→查成績” 的案例,用 async/await 改寫后,代碼更簡潔:

// 封裝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版本:同步寫法,異步執(zhí)行
async function getStudentInfo() {
  try {
    const user = await login(); // 登錄
    const course = await getCourse(user.id); // 查課程
    const score = await getScore(course.id); // 查成績
    console.log("最終成績:", score); // 輸出:{ score: 90 }
  } catch (error) {
    console.log("出錯(cuò)了:", error);
  }
}

getStudentInfo();

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

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

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

// 錯(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è)異步操作互不依賴(比如同時(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秒
}

?? 核心原則:

  • 異步操作有依賴(比如查成績依賴課程 ID):用 await 串行執(zhí)行;
  • 異步操作無依賴:用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 的常見坑與注意事項(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 秒);
    ? 解決無依賴的異步操作,用Promise.all并發(fā)執(zhí)行。

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

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

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

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

核心語法:

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

使用原則:

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

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

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

相關(guān)文章

  • js觸發(fā)onchange事件的方法說明

    js觸發(fā)onchange事件的方法說明

    怎么在js代碼模式通過代碼觸發(fā)textbox控件的onchange事件呢,經(jīng)過查閱資料,發(fā)現(xiàn)js提供了一個(gè)方法,可以觸發(fā)控件的應(yīng)該是所有事件
    2014-03-03
  • JavaScript常用對(duì)象的方法和屬性小結(jié)

    JavaScript常用對(duì)象的方法和屬性小結(jié)

    JavaScript是基于對(duì)象的語言。我們知道,對(duì)象是由一組數(shù)據(jù)(JavaScript中稱之為屬性)和施加在這組數(shù)據(jù)上的方法組成的。JavaScript中還有一些不屬于任何對(duì)象的系統(tǒng)函數(shù)
    2012-01-01
  • JS簡單實(shí)現(xiàn)仿百度控制臺(tái)輸出信息效果

    JS簡單實(shí)現(xiàn)仿百度控制臺(tái)輸出信息效果

    這篇文章主要介紹了JS簡單實(shí)現(xiàn)仿百度控制臺(tái)輸出信息效果,涉及javascript中console.log函數(shù)的簡單使用技巧,需要的朋友可以參考下
    2016-09-09
  • 微信小程序?qū)崿F(xiàn)圓心進(jìn)度條

    微信小程序?qū)崿F(xiàn)圓心進(jìn)度條

    這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)圓心進(jìn)度條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • 如何判斷出一個(gè)js對(duì)象是否一個(gè)dom對(duì)象

    如何判斷出一個(gè)js對(duì)象是否一個(gè)dom對(duì)象

    如何判斷出一個(gè)js對(duì)象是否一個(gè)dom對(duì)象呢?下面小編就為大家?guī)硪黄袛喑鲆粋€(gè)js對(duì)象是否一個(gè)dom對(duì)象的方法。希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2016-11-11
  • JavaScript空值合并運(yùn)算符??使用場景

    JavaScript空值合并運(yùn)算符??使用場景

    這篇文章主要介紹了JavaScript空值合并運(yùn)算符??的相關(guān)資料,??是一個(gè)js邏輯操作符,當(dāng)左側(cè)的操作數(shù)為null或者undefined時(shí),返回其右側(cè)數(shù),否則返回左側(cè)數(shù),文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-09-09
  • 琥珀無限級(jí)聯(lián)動(dòng)菜單-JavaScript版

    琥珀無限級(jí)聯(lián)動(dòng)菜單-JavaScript版

    琥珀無限級(jí)聯(lián)動(dòng)菜單-JavaScript版...
    2006-11-11
  • 原生js實(shí)現(xiàn)日期計(jì)算器功能

    原生js實(shí)現(xiàn)日期計(jì)算器功能

    這篇文章主要為大家詳細(xì)介紹了原生js實(shí)現(xiàn)日期計(jì)算器功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • Javascript中的單目運(yùn)算符、雙目運(yùn)算符和多目運(yùn)算符操作大全

    Javascript中的單目運(yùn)算符、雙目運(yùn)算符和多目運(yùn)算符操作大全

    文章詳細(xì)介紹了JavaScript中的單目運(yùn)算符、雙目運(yùn)算符和三元運(yùn)算符,并提供了每個(gè)運(yùn)算符的示例和陷阱規(guī)避方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2026-03-03
  • JavaScript學(xué)習(xí)筆記之惰性函數(shù)示例詳解

    JavaScript學(xué)習(xí)筆記之惰性函數(shù)示例詳解

    函數(shù)是js世界的一等公民,js的動(dòng)態(tài)性、易變性在函數(shù)的應(yīng)用上,體現(xiàn)的淋漓盡致。下面這篇文章主要給大家介紹了關(guān)于JavaScript學(xué)習(xí)筆記之惰性函數(shù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。
    2017-08-08

最新評(píng)論

邵东县| 井陉县| 清河县| 霍林郭勒市| 东乌珠穆沁旗| 游戏| 融水| 绥棱县| 黄梅县| 苗栗县| 阿瓦提县| 西青区| 和政县| 公主岭市| 嘉黎县| 曲靖市| 黎川县| 博野县| 荣昌县| 临沭县| 长海县| 府谷县| 华容县| 洛阳市| 北京市| 光山县| 临漳县| 当阳市| 云霄县| 盐池县| 丁青县| 吉林省| 揭西县| 六安市| 镇雄县| 杭锦后旗| 永仁县| 平顺县| 济南市| 小金县| 柞水县|