🔥别再只会写回调了!一文彻底搞懂 Promise 与 async/await🔥

137 阅读9分钟

前言

还记得你第一次写异步代码时的痛苦吗?回调嵌套、逻辑混乱、代码难以维护……尤其是当“订个披萨”都能写出“回调地狱”的时候,你就会明白:只用回调,已经不够用了。

为了解决这些问题,JavaScript 引入了 Promise,并在此基础上发展出了更优雅的 async/await 语法。它们让异步编程更清晰、更简洁、更接近同步代码的写法。

拿吃披萨的例子来说,用Promiseasync/await写出来分别是这样的:

// 步骤一:选择披萨口味
function selectToppings() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const toppings = "培根 + 蘑菇";
      console.log("已选择披萨口味:" + toppings);
      resolve(toppings);
    }, 500);
  });
}

// 步骤二:制作披萨
function makePizza(toppings) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("披萨正在制作中...");
      resolve(toppings);
    }, 2000);
  });
}

// 步骤三:准备饮料
function prepareDrink() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("饮料准备完成");
      resolve();
    }, 1000);
  });
}

// 步骤四:叫朋友来
function callFriend() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("朋友到了");
      resolve();
    }, 1000);
  });
}

// Promise 链式调用
selectToppings()
  .then(toppings => makePizza(toppings))
  .then(() => prepareDrink())
  .then(() => callFriend())
  .then(() => {
    console.log("披萨、饮料、朋友都齐了,开饭啦 🍕");
  })
  .catch(error => {
    console.error("出错了:", error);
  });
// async await调用
async function orderDinner() {
    try {
        const toppings = await selectToppings();
        await makePizza(toppings);
        await prepareDrink();
        await callFriend();
        console.log("披萨、饮料、朋友都齐了,开饭啦 🍕");
    } catch (error) {
        console.error("出错了:", error);
    }
}
// 执行
orderDinner();

比回调函数可读性高多了吧hhhhhh~

本文将带你从回调函数出发,一步步走向 Promiseasync/await ,让你彻底告别“回调地狱”,写出更现代、更优雅的异步代码。

Promise

Promise是什么?

Promise 是一个对象,表示一个异步操作的最终完成(或失败)及其结果值。

换句话说,Promise 可以看作是一个容器,里面保存着一个未来才会结束的操作(通常是异步操作)的结果。

就像一个没有兑现的承诺,你不知道这个承诺以后会不会兑现~

Promise 的三种状态

Promise 对象有三种状态:

状态含义
pending(等待中)初始状态,既没有被兑现,也没有被拒绝
fulfilled(已兑现)操作成功完成,调用 resolve()
rejected(已拒绝)操作失败,调用 reject()

一旦状态改变,就不会再变(状态不可逆)。

Promise 的基本结构

看着是挺清晰,我们来一个例子看看Promise到底长什么样吧!

const myPromise = new Promise((resolve, reject) => {
    // 异步操作
    if (操作成功) {
        resolve("成功的结果");  // 调用 resolve 代表成功
    } else {
        reject("失败的原因");   // 调用 reject 代表失败
    }
});

首先解析一下Promise的语法: Promise是一个对象,可以用new来新建一个,其中在建立Promise对象的时候,我们要传入一个函数,这个函数包含两个形参,分别是resolvereject(当然你可以随便取名),而这两个形参不需要你传入任何值,因为JS引擎在执行的时候就传入好了,这两个值分别对应JS引擎传入的两个函数。 第一个值对应着Promise成功时调用的函数,第二个值对应Promise失败时调用的函数。最后,在函数内部,我们可以进行异步操作,操作完成后根据你编写的逻辑,来决定返回resolve还是reject.

最开始刚声明完毕的时候是这个样子:

屏幕截图 2025-08-08 145142.png

由于承诺的事情没有结果,所以处于等待状态。

承诺成功履行时,变为<fullfilled>状态:

屏幕截图 2025-08-08 145545.png

承诺失败时,变为<rejected>状态:

屏幕截图 2025-08-08 145522.png

Promise 的使用方式

 .then() 和 .catch()

  • .then() 用于处理成功的结果(即 resolve()
  • .catch() 用于处理错误(即 reject()
myPromise
    .then(result => {
        console.log("成功:", result);
    })
    .catch(error => {
        console.error("失败:", error);
    });

注意: .then()方法中的形参result来自于它上一个Promise返回的结果,也就是resolve('')里面的结果。

.then() 

promise.then(onFulfilled, onRejected);
  • onFulfilled:当 Promise 成功(resolved)时调用的函数,接收一个参数(即成功的结果)
  • onRejected:当 Promise 失败(rejected)时调用的函数,接收一个参数(即失败的原因)

这两个参数都是可选的,不过你通常会传一个成功处理函数(onFulfilled)。

.then() 方法本身会返回一个新的 Promise对象

.catch()

promise.catch(Errfunction) // 当Promise为rejected的时候会触发并执行里面的函数

.catch() 最常见的用途是:

  • 捕获 Promise 链中任何 .then() 抛出的异常(下方链式调用有详细讲解)
  • 处理 Promise 被显式 reject()
  • 防止未处理的 rejected Promise

.finally()

其常见用途是:

其本身也会返回一个Promise,但是它不会接收任何参数,也就是它记不住之前Promise的结果,它无论如何都会执行,如果其本身抛出一个错误,那么就会覆盖原来的Promise的结果。

举个例子

模拟一个异步请求(比如请求数据)

function fetchData() {
    return new Promise((resolve, reject) => {
        try {
            fetch('https://api.weather.gov/gridpoints/OKX/35,35/forecast')
                .then(response => response.json())
                .then(data => resolve(data.properties.periods[1].shortForecast));
        } catch (error) {
            reject(error);
        }
    })
}
function displayData(weather) {
    console.log(weather);
}
function displayError(error){
    console.log(error)
}
fetchData()
    .then(displayData)
    .catch(displayError)

PS:这是外网一个网址,能够查询当地的天气,如果你想让这个结果显现,需要挂梯子哦~

Promise 链式调用(Chaining)

你可以通过 .then() 返回一个新的 Promise,从而实现链式调用,避免回调地狱。

function step1(x) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("Step 1:", x);
            resolve(x + 1);
        }, 1000);
    });
}

function step2(x) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("Step 2:", x);
            resolve(x * 2);
        }, 1000);
    });
}

function step3(x) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("Step 3:", x);
            resolve(x - 3);
        }, 1000);
    });
}

step1(5)
    .then(step2)
    .then(step3)
    .then(result => {
        console.log("最终结果是:", result);
    })
    .catch(error => {
        console.error("出错了:", error);
    });

Plus: 当Promise在某一个环节中产生了rejected状态后,则其后面的.then()方法都不会执行,会直接执行.catch(),抛出错误,.catch()能捕获整个链的错误。

现在你已经非常了解 Promise 的方方面面,包括它的状态、基本结构、链式调用和错误处理机制 。

现在我们要进入 异步编程的“终极进化形态” —— async/await

什么是 async/await

async/await 是基于 Promise 的语法糖,它让异步代码看起来更像同步代码,大大提升了代码的可读性和可维护性。

你可以把它看作是编写异步代码的“更优雅的方式”,也就是写的更好看了。嗯...就是这样

async

使用 async 关键字,你可以声明一个函数为 异步函数。它总是返回一个 Promise

举个例子

async function sayHello() {
    return "Hello, async!";
}

sayHello().then(message => console.log(message));

等价于:

function sayHello() {
    return Promise.resolve("Hello, async!");
}

输出 sayHello()结果为:

image.png

async可以包裹几乎所有函数,而且会让它们的返回值都变成Promise.

await

await 只能在 async 函数中使用,它会 暂停函数的执行,直到一个 Promise 被解决(fulfilled 或 rejected),然后返回 Promise 的结果。

举个例子

async function fetchUser() {
    const response = await fetch('https://api.example.com/user/1');
    const user = await response.json();
    return user;
}

这看起来是不是和同步代码差不多?我们一步步“等待”结果,不用再写 .then() 链了!

async/await 改写之前的获取天气状况的例子:

之前利用Promise写的已经比较清晰了,现在我们来用 async/await 把它变得更清晰:

async function fetchData() {
    try {
        const response = await fetch('https://api.weather.gov/gridpoints/OKX/35,35/forecast');
        const data = await response.json();
        return data.properties.periods[1].shortForecast;
    } catch (error) {
        throw error; // 或者 reject(error) 在 Promise 中
    }
}
async function main() {
    try {
        const result = await fetchData();
        console.log("拿到的结果是:", result);
    } catch (error) {
        console.error("出错了:", error);
    }
}

main();


是不是一眼就能看懂整个流程?没有 .then() 的嵌套,逻辑平铺直叙,就像写同步代码一样自然

async/await 的优势

特性描述
✅ 更清晰的逻辑避免了 Promise 链式调用的“缩进地狱”
✅ 更容易调试代码顺序和执行顺序一致
✅ 更容易处理异常可以用 try/catch 捕获错误
✅ 更现代是目前 JavaScript 异步编程的主流写法

⚠注意

1. async 和 await 必须一起使用

例外情况:JS 模块(ES Modules)和 Chrome 开发者控制台

想使用 await,必须在 async 函数内部,否则会报错。但在 Chrome 的控制台中(REPL 环境)和 ES 模块的顶级作用域(Top-level await)中可以例外地使用 await,无需包裹在 async 函数中。

2. async/await 只对返回 Promise 的函数生效

await 只对返回 Promise 的函数起作用。如果你 await 一个普通值(如字符串、数字),它会立刻返回这个值,不会等待。

3. 你可以 await 任何返回 Promise 的函数

不管这个函数是不是 async 函数,只要它返回一个 Promise,你就可以 await 它。

4. 任何函数都可以转换为 async 函数

包括普通函数、箭头函数、方法、构造函数等,只要加上 async 关键字,它就会变成一个返回 Promise 的异步函数。

5. 所有 async 函数都返回一个 Promise

所有 async 函数的返回值都会被自动封装成一个 Promise。即使你 return 123,它也会变成 Promise.resolve(123)

6. 使用 try/catch 进行错误处理

async/await 中,推荐使用 try/catch 来捕获异步错误,这样可以像同步代码一样处理异常,比 .catch() 更直观

总结

JavaScript 的异步编程经历了从 回调函数 → Promise → async/await 的不断演进,目的就是为了写出 更清晰、更易维护 的代码。

Promise是一个对象,它包含着一个状态,表示一个结果值,如果成功就调用resolve(),失败就调用reject()

其中Promise包含三个状态:

Promise还没有结果时,为Pending状态,表示等待

resolve()后变为fulfilled状态

reject()后变为rejected状态

Promise可以使用.then()方法进行下一步:

promise.then(onFulfilled, onRejected);
  • onFulfilled:当 Promise 成功(resolved)时调用的函数,接收一个参数(即成功的结果)
  • onRejected:当 Promise 失败(rejected)时调用的函数,接收一个参数(即失败的原因)

随后.then()也会返回一个Promise对象

Promise还能进行链式调用:

step1(5)
    .then(step2)
    .then(step3)
    .then(result => {
        console.log("最终结果是:", result);
    })
    .catch(error => {
        console.error("出错了:", error);
    });

可以不断将上一步的结果传递给下一步,可以用catch()获取reject()传递的错误信息。

顺嘴一说:还有个api叫.finally(),一般放在Promise链的最后,无论传递结果出不出错,这一句都会执行。

async/await是全新的api,是对Promise的优化,我们可以利用async/await代替Promise来执行异步任务。

以上就是全文了,如有错误,希望大家指出呀!拜拜!