第十四天 异步 JavaScript

发布于:2025-08-10 ⋅ 阅读:(17) ⋅ 点赞:(0)

 一、同步 VS 异步编程

1.同步编程

(1)特点:代码按照自上而下的顺序执行,每一行代码都必须等待上一行代码执行完毕才能开始。

(2) 示例代码:

JavaScript

console.log('第一步:开始做饭');
console.log('第二步:做好了米饭'); // 这行代码会等待第一行执行完
console.log('第三步:做好了菜'); // 这行代码会等待第二行执行完

2.异步编程

(1)特点:代码的执行顺序无关。当遇到一个耗时的操作(比如网络请求、文件读写),程序会先发起这个任务,然后立即跳过它,去执行后面的代码,等到耗时任务完成后,再回头处理它的结果。

(2)示例代码:

JavaScript

console.log('第一步:开始请求数据...');
// 模拟一个异步操作,比如网络请求
setTimeout(() => {
  console.log('第二步:数据请求成功!'); // 这行代码会在2秒后执行
}, 2000);
console.log('第三步:我已经不等了,继续执行后面的代码'); // 这行代码会立即执行

二、回调函数与回调地狱

1.回调函数(Callback)

(1)原理:将一个函数作为参数传递给另一个函数,当异步任务完成时,调用这个回调函数来处理结果。

(2)示例代码:

JavaScript

function greet(name) {
  console.log(`Hello, ${name}!`);
}

function processUserInput(callback) {
  const name = 'Alice';
  callback(name); // 调用传入的函数
}

processUserInput(greet); // 将 greet 函数作为参数传入

2.回调地狱(Callback Hell)

(1)原理:当有多个相互依赖的异步任务时,回调函数会一层层嵌套,形成“金字塔”结构.

(2)示例代码:

JavaScript

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doAnotherThing(newResult, function(finalResult) {
      console.log('最终结果是:' + finalResult);
    });
  });
});

(3)问题

难以阅读:代码向右横向拓展,嵌套层级非常深。

难以维护:如果你需要修改某个环节的逻辑,可能要逐层向上或向下追溯。

难以排错:错误处理逻辑散落在每一层回调中,不够集中。

三、Promise 的基础

1.概念:Promise 是一个代表异步操作最终完成(或失败)的对象。

(1)当你发起一个异步请求时,你会立即得到一个 Promise 对象,它代表着这个请求的结果(但结果还没出来)。

(2)这个 Promise 对象承诺你,当异步操作完成时,他会通知你成功的结果(fulfilled),或者失败的原因(rejected)。

(3)在这个结果出来之前,Promise 对象处于一个“待定”的状态。

2.Promise 的状态

(1)Pending(待定):初始状态,异步操作正在进行中。

(2)Fulfilled(已成功):异步操作成功完成,并返回了一个结果值。

(3)Rejected(已失败):异步操作失败,并返回了一个失败的原因。

(4)Promise 的状态一旦从 pending 变为 fulfilled 或 rejected,就不可逆转,也不会再改变。

3.如何创建和使用 Promise

(1)创建 Promise:通过  new Promise() 构造函数来创建一个 Promise 对象。它接受一个函数作为参数,这个函数有两个参数:

  • resolve:一个函数,用于在异步操作成功时调用,并将结果作为参数传递。

  • reject:一个函数,用于在异步操作失败时调用,并将错误作为参数传递。

    示例代码:

JavaScript

const myPromise = new Promise((resolve, reject) => {
  // 模拟一个异步操作,比如从服务器获取数据
  setTimeout(() => {
    const success = true; // 假设操作成功

    if (success) {
      resolve('数据获取成功!'); // 异步操作成功,调用 resolve
    } else {
      reject('数据获取失败:网络错误'); // 异步操作失败,调用 reject
    }
  }, 1000);
});

(2)使用 Promise: .then().catch()

         创建 Promise 后,你需要使用.then().catch() 方法来处理异步操作的结果。

  • .then():用于处理 Promise 成功时的结果。它接收一个回调函数,这个回调函数的参数就是 resolve 传递过来的值。

  • .catch():用于处理 Promise 失败时的原因。它接收一个回调函数,这个回调函数的参数就是 reject 传递过来的错误。

        示例代码:

JavaScript

// 使用 myPromise 对象
myPromise
  .then((result) => {
    // 异步操作成功时执行这里的代码
    console.log('成功啦!', result); // 输出: "成功啦! 数据获取成功!"
  })
  .catch((error) => {
    // 异步操作失败时执行这里的代码
    console.error('出错了:', error);
  });

4.Promise 链式调用:解决回调地狱

(1)Promise 最强大的功能就是链式调用。每个.then() 方法都会返回一个新的 Promise,这允许我们将多个异步操作按顺序串联起来,从而摆脱回调地狱。

(2)当你在一个 .then() 回调函数中返回一个值,这个值会作为下一个 .then() 的参数。如果返回的是一个新的 Promise 对象,那么下一个 .then() 会等待这个新的 Promise 完成。

(3)示例代码:

JavaScript

function step1() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('第一步完成');
      resolve(10); // 返回一个数字
    }, 1000);
  });
}

function step2(prevResult) {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('第二步完成,拿到上一步结果:', prevResult);
      resolve(prevResult * 2); // 返回新结果
    }, 1000);
  });
}

function step3(prevResult) {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('第三步完成,拿到上一步结果:', prevResult);
      resolve('所有步骤都完成了!');
    }, 1000);
  });
}

// 链式调用
step1()
  .then((result) => step2(result)) // result 是 step1 的返回值
  .then((result) => step3(result)) // result 是 step2 的返回值
  .then((finalResult) => {
    console.log('最终结果:', finalResult); // finalResult 是 step3 的返回值
  })
  .catch((error) => {
    console.error('链式调用中出现错误:', error);
  });

四、async/await

1.概念:async/await是 JavaScript 中处理异步操作的一种现代化、更优雅的方式。它是建立在 Promise 的基础之上的,但通过一种更直观的语法,让异步代码看起来就像同步代码一样。这极大地提高了代码的可读性和可维护性。

2. async 关键字:让函数变为异步

(1)当你把async关键字放在一个函数面前时,这个函数就成为了一个异步函数。

(2)核心特性:async函数会始终返回一个 Promise 对象。

  • 如果你在async函数中返回一个普通值,JavaScript 会自动将其包装在一个成功的 Promise 中
  • 如果你在async函数中抛出一个错误,JavaScript 会自动将其包装在一个失败的 Promise 中

(3)示例代码:

JavaScript

// 这是一个普通的异步函数
async function getGreeting() {
  return 'Hello, async!';
}

// 调用它,得到一个 Promise
getGreeting().then(result => {
  console.log(result); // 输出: 'Hello, async!'
});

3.await 关键字:等待异步操作完成

(1)作用: await 关键字只能在 async函数内部使用。他会“暂停”async函数的执行,直到紧跟在它后面的 Promise 得到解决(成功或失败)。

(2)核心特性:

  • await等待的 Promise 成功时,它会返回 Promise 的结果值。
  • 当await等待的 Promise 失败时,他会抛出一个错误,就像同步代码中 throw 一样。
  • await关键字使得我们可以用顺序的、同步的思维来编写异步代码。

(3)示例代码:

JavaScript

function fetchUserData() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ name: 'Alice', id: 1 });
    }, 1000); // 模拟1秒后返回数据
  });
}

// 使用 async/await
async function showUserData() {
  console.log('开始获取用户数据...');
  // await 会暂停函数执行,直到 fetchUserData() 的 Promise 成功
  const user = await fetchUserData();
  console.log('数据获取成功!');
  console.log('用户名:', user.name);
}

showUserData();

4.如何处理错误

(1)async/await 结合了 Promise 的错误处理机制,但使用了更熟悉的 try...catch 语法,这让错误处理变得非常直观。

  • 使用 try...catch 你可以用一个 try...catch 块将 await 调用包裹起来。如果 await 等待的 Promise 失败了,它会抛出一个错误,catch 块就能捕获到这个错误。

(2)示例代码:

JavaScript

function fetchFailedData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('数据获取失败:网络超时'); // 模拟失败
    }, 1000);
  });
}

async function getFailedData() {
  try {
    console.log('开始尝试获取数据...');
    const data = await fetchFailedData();
    console.log('数据获取成功:', data); // 这行代码不会执行
  } catch (error) {
    console.error('出错了:', error); // 捕获到 Promise 失败的错误
  }
}

getFailedData();

5.async/await 的优点总结

(1)可读性极高:异步代码看起来像同步代码,逻辑更清晰,更容易理解和维护。

(2)解决了回调地狱:它让复杂的异步流程变得扁平化,避免了层层嵌套。

(3)更简洁的错误处理:使用 try...catch 捕获错误,这是一种被开发者广泛接受的、直观的           错误处理方式。

(4) 更好的调试体验:在 async函数中使用断点,调速器可以像对待同步代码一样,逐行暂停       和执行,这比调试 Promise 链要方便得多。


网站公告

今日签到

点亮在社区的每一天
去签到