js异步:Promise Async

129 阅读7分钟

一、什么是异步?

我们先来理解一个词:同步 vs 异步

  • 同步(Synchronous) :代码一行一行执行,上一行没完成,下一行就不能动。
  • 异步(Asynchronous) :代码可以跳过等待的部分,先去执行后面的,再回来处理。

举个例子

你打电话点外卖:

  • 同步:你一直拿着电话等饭送来,你啥都不能干。
  • 异步:你打完电话,继续做作业,饭来了再叫你。

二、为什么需要异步?

因为有些事情很,比如:

  • 从服务器请求数据
  • 读写文件
  • 计时器(setTimeout)
  • 网络请求(如 fetch

如果这些事情是同步的,页面会卡住,用户体验很差。 所以 JavaScript 提供了异步机制。


三、常见的异步方式

1. setTimeout —— 最早的异步写法

console.log("1");
setTimeout(() => {
  console.log("2");
}, 1000);
console.log("3");

输出顺序是:

1
3
2   // 延迟1秒后才执行

setTimeout 会把函数“挂起来”,等时间到了再执行。


2. 回调函数(Callback)

function doAsync(callback) {
  setTimeout(() => {
    console.log("Async task done");
    callback(); // 任务完成后执行回调
  }, 1000);
}
​
doAsync(() => {
  console.log("Callback executed");
});

缺点:回调地狱(callback hell) ,嵌套太多可读性差。


3. Promise —— 解决回调地狱

Promise 是一种“承诺”式的对象,告诉你任务将来会完成。

let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("任务完成");
  }, 1000);
});
​
promise.then(result => {
  console.log(result); // 输出“任务完成”
});

resolve 表示成功,reject 表示失败。


4. async/await —— 最现代、最舒服的写法

asyncawait 是对 Promise 的语法糖,让异步代码看起来像同步。

function waitOneSecond() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("1秒后完成");
    }, 1000);
  });
}
​
async function main() {
  console.log("开始");
  let result = await waitOneSecond();  // 等待1秒
  console.log(result);
  console.log("结束");
}
​
main();

输出:

开始
(等1秒)
1秒后完成
结束

四、总结

方法优点缺点
回调函数简单、基础容易嵌套、难维护
Promise避免回调地狱链式写法较繁琐
async/await最清晰、最直观只适用于返回 Promise 的函数

一、Promise 是什么?

Promise 是 JavaScript 中的一种异步编程解决方案,它代表一个未来才会结束的操作(可能成功,也可能失败)

用一句话概括: 👉 Promise 是一个装着未来结果的盒子


二、Promise 的三种状态

Promise 一旦创建,它的状态就会在以下三种之间切换:

  1. pending(等待中) :初始状态,还没有结果。
  2. fulfilled(已完成) :操作成功,得到了结果。
  3. rejected(已拒绝) :操作失败,返回错误。

状态一旦变成 fulfilled 或 rejected,就不能再变了!


三、Promise 的基本写法

创建 Promise

const myPromise = new Promise((resolve, reject) => {
  // 模拟异步操作,比如网络请求
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve("成功啦!");
    } else {
      reject("出错了!");
    }
  }, 1000);
});
  • resolve(value):代表成功,传出数据。
  • reject(reason):代表失败,传出错误信息。

使用 .then().catch()

myPromise
  .then(result => {
    console.log("结果:", result);
  })
  .catch(error => {
    console.error("错误:", error);
  });
  • .then() 是处理成功的回调
  • .catch() 是处理失败的回调

四、Promise 链式调用

.then() 本身也返回一个 Promise,可以继续 .then() 下去:

new Promise((resolve) => {
  resolve(2);
})
  .then((num) => {
    console.log(num);  // 2
    return num * 2;
  })
  .then((num) => {
    console.log(num);  // 4
    return num * 2;
  })
  .then((num) => {
    console.log(num);  // 8
  });

每个 .then() 的返回值会传给下一个 .then()


五、Promise 的静态方法

1. Promise.resolve(value)

快速创建一个成功状态的 Promise:

Promise.resolve("OK").then(res => {
  console.log(res); // OK
});

2. Promise.reject(reason)

快速创建一个失败状态的 Promise:

Promise.reject("失败").catch(err => {
  console.error(err); // 失败
});

3. Promise.all([])

多个 Promise 并发,全部成功才算成功

Promise.all([
  Promise.resolve(1),
  Promise.resolve(2),
  Promise.resolve(3)
]).then(res => {
  console.log(res); // [1, 2, 3]
});

如果有一个失败,就会直接进入 catch

4. Promise.race([])

谁先完成,就用谁的结果(最快的那个):

Promise.race([
  new Promise(resolve => setTimeout(() => resolve("A"), 1000)),
  new Promise(resolve => setTimeout(() => resolve("B"), 500))
]).then(res => {
  console.log(res); // B
});

六、和 async/await 的关系

async/await 是 Promise 的语法糖,写起来更像同步:

function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
​
async function run() {
  console.log("等待 1 秒...");
  await wait(1000);
  console.log("时间到了!");
}
​
run();

所有 await 后面跟的,都是一个 Promise。


七、常见错误和小提示

错误示例原因
忘记 .catch()会导致错误没有被捕获
Promise 嵌套写太多 .then()可读性差,建议用 async/await
reject() 不代表抛出异常它只是把 Promise 状态变成失败,catch 才能捕捉
用了 async/await 却没 try/catch一旦出错,程序会崩溃

八、一个完整的小例子

function fakeRequest(data) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (data === "hello") {
        resolve("返回成功数据");
      } else {
        reject("出错啦!");
      }
    }, 1000);
  });
}
​
fakeRequest("hello")
  .then(res => {
    console.log("成功:", res);
  })
  .catch(err => {
    console.error("失败:", err);
  });

如果你愿意,我可以给你设计几个小练习题来巩固 Promise 的用法。你想试试吗?


一、async/await 是什么?

它是对 Promise 的一种更清晰、更优雅的写法方式

  • async:声明一个函数为异步函数
  • await:只能在异步函数内部使用,表示等待 Promise 执行完再继续

📌 本质:async/await 是 Promise 的语法糖(语法更好看,本质还是 Promise)


二、为什么要用 async/await

.then().then().catch() 写 Promise,很容易写出“回调地狱”:

doA()
  .then(res => doB(res))
  .then(res => doC(res))
  .catch(err => console.error(err));

但用 async/await 就很清晰:

async function main() {
  try {
    const a = await doA();
    const b = await doB(a);
    const c = await doC(b);
    console.log(c);
  } catch (err) {
    console.error(err);
  }
}
main();

三、基本语法详解

1. 声明一个异步函数

async function hello() {
  return "你好,世界";
}

等价于:

function hello() {
  return Promise.resolve("你好,世界");
}

所以:async 函数一定返回一个 Promise


2. 使用 await 等待 Promise 结果

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function sayHi() {
  console.log("开始等待");
  await delay(1000);
  console.log("1秒过后");
}

sayHi();

执行结果:

开始等待
(等待1秒)
1秒过后

📌 await 会“暂停”这个函数的执行,直到 Promise 完成,再继续往下走。

函数前的关键字 await 使函数等待 promise。
关键字 await 让 JavaScript 引擎等待直到 promise 完成(settle)并返回结果。
await 关键字只能在 async 函数中使用。


四、处理错误(try...catch)

await 过程中,如果 Promise 被 reject 了,就会抛出异常。

你需要用 try...catch 把它包住,防止程序崩掉:

async function fetchData() {
  try {
    const res = await fetch("https://api.example.com/data");
    const json = await res.json();
    console.log(json);
  } catch (err) {
    console.error("请求出错:", err);
  }
}

五、常见组合用法

1. 多个 await 顺序执行(串行)

const a = await task1();
const b = await task2(a);
const c = await task3(b);

每一步都要等前一步完成。


2. 并行执行(更快!)

const [res1, res2] = await Promise.all([
  fetchData1(),
  fetchData2()
]);

多个任务一起开始执行,全部完成后再拿结果。


六、async/await 常见问题

问题说明
await 外面不能用await 必须写在 async 函数里面
忘记 try/catch一旦出错,整个函数会抛异常
不会自动等待多个异步需要 Promise.all() 来并发等待
async 函数默认返回 Promise不是“立即返回值”,而是“承诺返回值”

七、一个真实例子:顺序执行任务

function wait(ms, msg) {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(msg);
      resolve();
    }, ms);
  });
}

async function runTasks() {
  await wait(1000, "任务1完成");
  await wait(500, "任务2完成");
  await wait(200, "任务3完成");
  console.log("全部完成");
}

runTasks();

输出:

(1秒后)任务1完成
(0.5秒后)任务2完成
(0.2秒后)任务3完成
全部完成

八、总结

特点说明
更清晰写起来像同步代码
更易维护逻辑清晰,错误更好捕获
本质是 Promiseasync 函数返回 Promise,await 等待 Promise

函数前面的关键字 async 有两个作用:

  1. 让这个函数总是返回一个 promise。
  2. 允许在该函数内使用 await

Promise 前的关键字 await 使 JavaScript 引擎等待该 promise settle,然后:

  1. 如果有 error,就会抛出异常 —— 就像那里调用了 throw error 一样。
  2. 否则,就返回结果。

这两个关键字一起提供了一个很好的用来编写异步代码的框架,这种代码易于阅读也易于编写。

有了 async/await 之后,我们就几乎不需要使用 promise.then/catch,但是不要忘了它们是基于 promise 的,因为有些时候(例如在最外层作用域)我们不得不使用这些方法。并且,当我们需要同时等待需要任务时,Promise.all 是很好用的。