为了账号安全,请及时绑定邮箱和手机立即绑定

从回调、Promise到async/await:详解JavaScript异步编程终极指南

学习使用回调、Promise 和 async/await 进行 JavaScript 异步编程,避免回调地狱,通过实际例子编写更简洁、更易维护的代码。

大家好,各位前端开发者们,今天咱们来聊聊javascript里头一个超级重要的概念:异步编程的概念。

  • 我将在Scribbler.live上首先创建我的代码片段,这是一个非常棒的平台,可以让你无需手动设置就能运行JavaScript笔记本、在线编译器和编辑器。
  • 此外,我还将提供一个链接,该链接包含所有代码示例,你可以直接打开并运行代码片段来自行查看结果。
  • 我将使用scrib.show,这是来自Scribbler.live的一个功能,它类似于JavaScript中的console.log

咱们直接来吧

目录:

  • 什么是回调

  • 基本示例

  • 异步回调

  • 回调地狱

  • Promise

  • 基本示例

  • 获取数据

  • Promise链式调用

  • Promise方法(如then、catch等)

  • async await

  • 解决回调地狱

  • 获取数据
什么是回调呢?
  • 回调函数是指将一个函数作为参数传递给另一个函数,该函数随后会被执行(同步或异步),
  • 回调函数在JavaScript中用于处理异步任务、事件处理和函数式编程。

代码示例

基本例子

    function saveOrder(orderId, userMail, callback) {
        const order = {
          orderId,
          userMail
        }
        scrib.show(order)
        callback(userMail); // 调用回调函数
    }

    function sendOrderEmail(userMail) {
        scrib.show(userMail, "确认订单")
    }

    saveOrder("145908275","user@gmail.com", sendOrderEmail);
    // 这会记录订单并发送确认邮件给用户

全屏 退出全屏

异步回调

可以举例来说 setTimeout 方法,比如说它执行时是异步的。

    console.log("JavaScript 很酷");

    setTimeout(() => {
        console.log("这段代码会在2秒后执行");
    }, 2000);

    console.log("Scribbler 很酷");

    // 输出结果
    JavaScript 很酷
    Scribbler 很酷
    这段代码会在2秒后执行

进入全屏 退出全屏

回调地狱(灾难金字塔)

function saveOrder(orderId, userMail, callback) {
    const order = {
        orderId,
        userMail
    }
    scrib.show(order)
    callback(userMail); // 执行回调
}

function sendDetailsToSeller(orderId, userMail, callback) {
    const details = {
        orderId,
        userMail
    }
    scrib.show(details)
    callback(userMail);
}

function sendOrderEmail(userMail) {
    scrib.show(userMail, "您的订单已确认")
}

saveOrder("145908275", "user@gmail.com", () => sendDetailsToSeller("145908275", "user@gmail.com", sendOrderEmail));
// 可以看到,我们需要在多个层级传递回调以实现链式操作。

全屏 退出全屏

  • 当回调嵌套层次过深时,代码就会变得很难看懂和管理了。

🔴 问题:难以阅读、维护和调试,
✅ 解决方案:Promise 或 async/await.

Promise - 解决回调地狱问题的救星。
  • 在 JavaScript 中,Promise 表示异步操作未来的结果,即成功或失败。它提供了一种比传统回调更清晰的方式来处理异步任务。
  • 它接受一个有两个参数的函数:

  • resolve → 当异步任务成功时调用。

  • reject → 当异步任务失败时调用。
  • Promise 状态:一个 Promise 可以处于以下三种状态之一:

  • Pending 状态 → 初始状态,既未完成也未拒绝。

  • Fulfilled 状态 → 操作成功完成。

  • Rejected 状态 → 操作失败完成。

示例代码:

基本例子

    const basicPromise = new Promise((resolve, reject) => {
      setTimeout(() => {
        let success = true; // 模拟任务的成功或失败
        if (success) {
          resolve("任务完成成功啦!");
        } else {
          reject("任务失败了 ");
        }
      }, 2000);
    });

    basicPromise
      .then((result) => {
        scrib.show(result); // 显示: 任务完成成功啦!
      })
      .catch((error) => {
        scrib.show(error); // 显示: 任务失败了  (如果失败)
      })
      .finally(() => {
        scrib.show("Promise 执行完了。");
      });

切换到全屏 退出全屏

使用 fetch 抓取数据

    fetch('https://jsonplaceholder.typicode.com/todos/1')
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP 错误!状态码为:${response.status}`);
            }
            return response.json();
        })
        .then(data => console.log(data))
        .catch(error => console.log("抓取失败:", error));

点击进入全屏 点击退出全屏

链式调用

    // 使用 then 方法链式调用来处理异步操作
    new Promise((resolve) => {
      setTimeout(() => resolve(10), 1000);
    })
      .then((num) => {
        scrib.show(num); // 打印数字: 10
        return num * 2;
      })
      .then((num) => {
        scrib.show(num); // 打印数字: 20
        return num * 3;
      })
      .then((num) => {
        scrib.show(num); // 打印数字: 60
      });

切换到全屏,退出全屏

Promise 对象的方法

  • Promise.all - 等待所有 promises 都完成解析。如果有任何一个被拒绝,整个结果也会被拒绝。
  • Promise.race - 返回最先完成(解析或拒绝)的 promise。
  • Promise.allSettled - 等待所有 promises 完成(解析或拒绝),然后返回它们的状态。
  • Promise.any - 返回最先被解析成功的 promise,忽略拒绝的情况。
    const promise1 = new Promise((resolve) => {
      setTimeout(() => resolve(Math.floor(3.14)), 1000);
    })
    const promise2 = new Promise((resolve) => {
      setTimeout(() => resolve(Math.ceil(3.14)), 1000);
    })
    const promise3 = new Promise((resolve) => {
      setTimeout(() => resolve(Math.pow(3.14, 10)), 1000);
    })

    // Promise.all
    const promiseAll = Promise.all([promise1,promise2,promise3])
    promiseAll.then(scrib.show())

    // Promise.race
    const promiseRace = Promise.race([promise1,promise2,promise3])
    promiseRace.then(scrib.show())

    // Promise.allSettled
    const promiseAllSettled = Promise.allSettled([promise1,promise2,promise3])
    promiseAllSettled.then(scrib.show())

    // Promise.any
    const promiseAny = Promise.any([promise1,promise2,promise3])
    promiseAny.then(scrib.show())

切换到全屏 恢复正常视图

  • 为了更好地处理异步,可以使用 async/await,这使得处理 Promise 更加简单。
异步操作/Await
  • async/await 是现代 JavaScript 的一个特性,简化了处理异步代码。它允许你以同步代码的方式编写异步代码,使其更易于阅读、理解和调试。
  • 为什么要用 async/await 呢?

  • 在 async/await 之前,JavaScript 开发者处理异步代码的方法包括:

  • 回调(导致回调地狱)

  • Promise(虽然更好,但仍需要使用 .then() 来链接处理程序)

  • async/await 的工作原理 - 标记为 async 的函数会自动返回一个 Promise,即使它返回的是一个简单的值也一样。

示例代码

解决回调地狱

    // 异步 await 示例,解决回调地狱问题
    async function saveOrder(orderId, userMail) {
        const order = {
          orderId,
          userMail
        }
        scrib.show(order)
        return order
    }

    async function sendDetailsToSeller(orderId, userMail) {
      const details = {
          orderId,
          userMail
        }
        scrib.show(details)
        return details
    }

    async function sendOrderEmail(userMail) {
        scrib.show(userMail, "Order confirmed")
        return "Email sent"
    }

    async function processOrder(orderId, userMail) {
      await saveOrder(orderId, userMail);
      await sendDetailsToSeller(orderId, userMail);
      await sendOrderEmail(userMail);
    }

    processOrder("145908275","user@gmail.com");
    // 这样读起来更清晰,也更容易管理

进入全屏,退出全屏

数据抓取:

    // 使用 async/await 获取数据
    async function fetchData() {
      const response = await fetch('https://jsonplaceholder.typicode.com/todos/1'); //  等待 fetch 请求完成

      if (!response.ok) {
        throw new Error(`HTTP 错误!状态码:${response.status},请检查网络。`);
      }

      const data = await response.json(); //  等待 response.json()
      return data;
    }

    fetchData()
      .then(data => scrib.show("获取的数据如下:", data))
      .catch(error =>  scrib.show("错误信息:", error));

点击此处进入/退出全屏模式

使用 async/await 和循环来处理可读流

    // 以下代码示例展示如何结合异步/await与循环来获取待办事项数据
    async function 获取待办事项() {
      let url列表 = [
        "https://jsonplaceholder.typicode.com/todos/1",
        "https://jsonplaceholder.typicode.com/todos/2",
        "https://jsonplaceholder.typicode.com/todos/3"
      ];

      for (let url of url列表) {
        let response = await fetch(url);
        let data = await response.json();
        // 将数据展示出来
        scrib.show(data);
      }
    }

    获取待办事项();

进入全屏 / 退出全屏

试试这个嵌入,运行上面提到的代码示例。

app.scribbler.live

这篇帖子到这就结束了,如果文章还有需要改进的地方,请告诉我。也来看看Scribbler.live网站吧。

你可以联系我:

Instagram - Instagram:https://www.instagram.com/supremacism__shubh/
LinkedIn - LinkedIn:https://www.linkedin.com/in/shubham-tiwari-b7544b193/
邮箱 - shubhmtiwri00@gmail.com

下面的链接是我收款的地方,感谢你的支持👇👇
https://www.buymeacoffee.com/waaduheck

也别忘了看看这些帖子。

shubhamtiwari909 ## 带 CVA 和 Tailwind 的按钮组件文章 Shubham Tiwari · 发布 2024年2月12日 #nextjs #react #webdev #typescript

视频 08:22 shubhamtiwari909 ## 微前端技术 - React | Solid | Vue Tiwari Shubham · 2024年1月9日 #教程指南 #webdev #新手 #学习之路

摄像头 15:48 shubhamtiwari909 ## Codium | 开发者的AI助手Codium Shubham Tiwari ・ 2024年1月4日 #vscode #提升 #指南 #新手

视频相机 05:25 shubhamtiwari909 ## Zustand - 初学者指南 Shubham Tiwari ・ 2023年11月21日 #webdev #教程 #React #JavaScript

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

0 评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消