JavaScript 中的callback、promise 和 async
本文最后更新于 313 天前,如有失效请评论区留言。

前言

本节主要聊一聊 JavaScript 中异步编程的相关内容,在 JavaScript 的世界中,所有代码都是单线程执行的,无法同时执行多个任务。当需要执行一个可能耗时的操作,如文件读取、网络请求、用户的输入、定时器的回调等,这时需要使用异步编程的思想,确保不会阻塞程序的执行。

回调函数

假设现在有这样一个计算过程,对随机输入的一个数,需要做如下处理,

  1. 对该数进行除 2 取整处理
  2. 对于步骤 1 的结果进行平方处理
  3. 对于步骤 2 的结果进行取模 2 处理

如果我们使用回调函数要怎么写呢?

// 第一步:数字除以2
function divNumber(number, callback) {
  number = number / 2
  console.log('Step 1: division the number = ' + number);
  callback(number);
}

// 第二步:计算数字的平方
function squareNumber(number, callback) {
  const square = number * number;
  console.log('Step 2: Squared the number = ' + square);
  callback(square);
}

// 第三步:计算数字的取模2
function modSquare(square, callback) {
  const mod = square % 2;
  console.log('Step 3: Mod the square = ' + mod);
  callback(mod);
}

// 执行计算过程
function doOperation(num) {
  divNumber(num,(number) => {  
      squareNumber(number, (square) => {    
          modSquare(square, (result) => {
              console.log('Result:', result);
          });
      });
  });
}

doOperation(10)

// 输出
> Step 1: division the number = 5
> Step 2: Squared the number = 25
> Step 3: Mod the square = 1
> Result: 1

使用回调函数来解决这类执行过程依赖于上一步骤的结果的事件时,有时候会比较麻烦,尤其是当步骤的程序更复杂的情形时,就会陷入一种回调地狱

为了解决这类问题,ES6之后,JavaScript 引入了 Promise 这个新特性,使得 js 引擎可以主动执行异步任务

Promise

关于 Promise 的相关 API 可以参考MDN Promise

Promise 是 JavaScript 中用于处理异步操作的一种机制,它使得异步代码更易于编写和管理。一个 Promise 可以处于以下三种状态之一:

  1. Pending(进行中): Promise 的初始状态,表示异步操作还在进行中。
  2. Fulfilled(已成功): 异步操作成功完成,并返回一个值,这时 Promise 进入 Fulfilled 状态。
  3. Rejected(已失败): 异步操作失败,返回一个错误信息,这时 Promise 进入 Rejected 状态。

new Promise 的构造函数接受一个函数作为参数,这个函数有两个参数,分别是 resolvereject。这个函数会在 Promise 被创建时立即执行。下面是基本的语法结构:

const myPromise = new Promise((resolve, reject) => {
  // 异步操作

  // 如果操作成功,调用 resolve 并传递成功的结果
  // resolve(value);

  // 如果操作失败,调用 reject 并传递失败的原因(通常是一个错误对象)
  // reject(reason);
});
  • resolve: 是一个函数,用于将 Promise 的状态从 Pending 转换为 Fulfilled,并传递一个值作为异步操作的成功结果。对应 then() 操作

  • reject: 是一个函数,用于将 Promise 的状态从 Pending 转换为 Rejected,并传递一个原因(通常是一个错误对象)作为异步操作的失败原因。对应 cache() 操作

new Promise 的函数体内,你可以执行异步操作,当异步操作完成时,调用 resolvereject,从而改变 Promise 的状态。这就是为什么 Promise 被用于处理异步操作,使得异步代码更易于管理和组织。

在第一节中回调函数案例改为 promise 怎么写呢?
我们给最后的结果加一个判断,如果算出来的结果等于0,则使用 resolve 函数处理表示处理完成,如果结果不等于0,则使用 reject 函数处理。表示处理失败,并给第一步和第二步加上延时,模拟计算过程

// 第一步:数字除以2
function divNumber(number) {
    return new Promise((resolve) => {
      setTimeout(function() {
        number = number / 2;
        console.log('Step 1: division the number = ' + number);
          resolve(number);
      }, 2000)    
  });

}

// 第二步:计算数字的平方
function squareNumber(number) {
  return new Promise((resolve) => {
    setTimeout(function() {
      const square = number * number;
      console.log('Step 2: Squared the number = ' + square);
      resolve(square);
    }, 1000)  
  });
}

// 第三步:计算数字的取模2
function modSquare(square) {
  return new Promise((resolve, reject) => {
    const mod = square % 2;
    console.log('Step 3: Mod the square = ' + mod);
    if (mod == 0) {
      resolve("result is zero")
    } else {
      reject("result is Non-zero")
    }
    resolve(mod);
  });
}

// 执行计算过程
function doOperation(num) {
  divNumber(num)
    .then((number) => squareNumber(number))
    .then((square) => modSquare(square))
    .then((result) => {
      console.log('Result:', result);
    })
    .catch((error) => {
      console.error('Error:', error);
    })
    .finally(() => console.log('doOperation end'))
}

doOperation(10);

// 输出

Step 1: division the number = 5
Step 2: Squared the number = 25
Step 3: Mod the square = 1
Error: result is Non-zero
doOperation end

Promise 异步处理,如果是被 resolve 函数处理则会执行 then 块,then 接受的参数就是 resove 抛出去的值;如果是被 reject 函数处理,则会执行 cache 块,最后无能程序执行结果如何,都会执行 finally 块,结构有点像 java 中的 try cache finally

async

async 函数是使用async关键字声明的函数。async 函数是  AsyncFunction 构造函数的实例,并且其中允许使用 await 关键字。async 和 await 关键字让我们可以用一种更简洁的方式写出基于 Promise 的异步行为,而无需刻意地链式调用 promise

再把之前的例子改些成 async 的方式实现

// 第一步:数字除以2
function divNumber(number) {
    return new Promise((resolve) => {
      setTimeout(function() {
        number = number / 2;
        console.log('Step 1: division the number = ' + number);
          resolve(number);
      }, 2000)    
  });

}

// 第二步:计算数字的平方
function squareNumber(number) {
  return new Promise((resolve) => {
    setTimeout(function() {
      const square = number * number;
      console.log('Step 2: Squared the number = ' + square);
      resolve(square);
    }, 1000)  
  });
}

// 第三步:计算数字的取模2
function modSquare(square) {
  return new Promise((resolve) => {
    const mod = square % 2;
    console.log('Step 3: Mod the square = ' + mod);
    resolve(mod);
  });
}

// 执行计算过程
async function doOperation(num) {
  try {
    const number = await divNumber(num);
    const square = await squareNumber(number);
    const result = await modSquare(square);
    console.log('Result:', result);
  } catch (error) {
    console.error('Error:', error);
  }
}

doOperation(10);

// 输出
> Step 1: division the number = 5
> Step 2: Squared the number = 25
> Step 3: Mod the square = 1
> Result: 1

异步编程思想在现代的 web 开发中非常的使用,我们需要熟练的掌握相关功能的使用,尤其是 Promise 和 async 的理解

版权声明:除特殊说明,博客文章均为Gavin原创,依据CC BY-SA 4.0许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇