作为iOS的研发人员,转向鸿蒙平台,学习ArkTS时,对于在函数前面加async的理解一直是懵懵的。一直是当作可异步执行的函数。

但是,对于调用异步函数时,前面加 await的这种写法,该函数也必须是async就不太理解了。

本篇内容是《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》这本书第四章内容的延续,是咱这本书读者的福利,在本篇内容中通过两张表格对比及三组示例说明在不同的场景中,async函数与普通函数的区别,每组示例可以独立的编译及调试,欢迎大家一同来深入的解这两者,甚至可以当作面试题来学习。

打个广告,对本书感兴趣的同学可以点击以下链接进行购买,或者了解我的班级参加 班级共同学习,点击链接可进入(华为官方活动)

【鸿蒙生态共建】一文两表三实例讲清async函数与普通函数的区别-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利_函数调用

编辑

往期福利:

【页面路由导航】三步实现页面跳转的完整示例-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利

【鸿蒙生态共建】一文10个示例讲懂鸿蒙系统ArkTS中的null与undefined的区别与应用-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利

核心区别速查表

参考多方的资料和代码实践,将个人的理解进行了整理。为了更好地理解,下面先在实现、执行、返回值、异常处理及调用等层面进行对比:

特性

普通函数 (无 async)

async 函数

实现约束

不能使用 await 关键字

可以使用 await 关键字来暂停执行

执行流程

同步执行:从第一行执行到最后一行,不会中途暂停

异步执行:遇到 await 会暂停,将线程控制权交还,直到等待的 Promise 解决

返回值

直接返回一个明确的值(如 numberstringobject 等)

总是返回一个 Promise 对象

异常处理

使用 try...catch 捕获同步错误

使用 try...catch 捕获同步错误 await 表达的 Promise 的拒绝(rejection)

调用方式

直接使用变量接收返回值:const result = func()

必须使用 .then()/.catch()  在另一个 async 函数中用 await 调用:const result = await func()


详细解释与代码示例

1. 返回值不同:Value vs. Promise

  • 普通函数:返回的就是 return 语句指定的值。
// 函数定义
function normalFunction() {
  return 58; // 直接返回数字 58
}
// 函数调用,result的值是58 (类型是 number)
const result = normalFunction();
  • async 函数:无论你返回什么,它都会用一个 Promise 对象把你返回的值“包裹”起来。
// 函数定义
async function asyncFunction() {
  // 等价于 return Promise.resolve(58);
  return 58; 
}
// 函数调用,result的值是一个Promise类型(感兴趣的可以调试一下,还没找到方法通过log输出Promise实例)
const result = asyncFunction();
// 之后通过result获取返回值:
result.then((value) => {
  // value: 58
  console.log("俩毛豆工具集:" + value);
}); 
// 上述代码也可以写成,这是不是就眼熟了
asyncFunction().then((value) => {
  // value: 58
}); 
// 或者在另一个通过await 以同步的方式执行该异步函数
// value 58
const value = await asyncFunction();

关于上述代码中,const result = asyncFunction(); // 函数调用,result的值是一个Promise类型(感兴趣的可以调试一下,还没找到方法通过log输出Promise实例),载图如下:

【鸿蒙生态共建】一文两表三实例讲清async函数与普通函数的区别-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利_执行流程_02

执行Promise时,得到的结果为58,截图如下:

【鸿蒙生态共建】一文两表三实例讲清async函数与普通函数的区别-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利_App_03

2. 执行流程不同:同步 vs. 可暂停的异步

  • 普通函数:执行是同步连续的。函数体内的代码会一口气执行完毕,期间不会被打断(除非有异常)。它会阻塞后面代码的执行,直到自己完全跑完。
// 同步函数,耗时约1秒
 function syncDelayOneSecond(): void {
   const startTime = new Date().getTime();
   let count = 0;
   // 通过空循环消耗CPU时间
   while (new Date().getTime() - startTime < 1000) {
     count++; // 空操作,仅用于消耗时间
   }
   console.log(`同步耗时完成,循环次数: ${count}`);
 }
function normalFunction() {
  console.log('第一步');
   syncDelayOneSecond()
  console.log('第二步'); // 紧接着执行
}
console.log('开始');
normalFunction();
console.log('结束');

上述的代码执行,Log输出,截图如下,注意一下左侧的时间列:

【鸿蒙生态共建】一文两表三实例讲清async函数与普通函数的区别-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利_函数调用_04

  • async 函数:执行是异步的。虽然它也会立即执行,但一旦遇到 await 关键字,它会暂停自己的执行,交出线程控制权,让事件循环(Event Loop)去执行其他代码(比如处理UI点击、网络响应等)。它会一直等到 await 后面的 Promise 变成“已解决”(fulfilled)或“已拒绝”(rejected)状态后,才恢复执行。
// fetchData() 是一个返回 Promise 的函数,1秒后获取到数据
async function fetchData() {
  const promise: Promise<number> = new Promise((resolve: Function, reject: Function) => {
    setTimeout(() => {
      resolve(88);
    }, 1000);
  })
  return promise;
}
async function asyncFunction() {
  console.log('第一步');
  let result = await asyncDelayOneSecond();
  console.log('第二步'); // 恢复执行
  return result;
}
console.log('开始');
asyncFunction().then((value)=>{
  console.log("俩毛豆工具集:" + value);
});
console.log('结束');

这种“暂停-恢复”的特性使得用同步代码的书写方式来处理异步操作成为可能,代码更清晰,避免了“回调地狱”(Callback Hell)。

  • 上述的代码执行,Log输出,截图如下,注意一下左侧的时间列,执行的顺序也与非async函数不同:

【鸿蒙生态共建】一文两表三实例讲清async函数与普通函数的区别-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利_App_05

3. 错误处理

  • 普通函数:使用 try...catch 只能捕获同步错误。
function normalFunction() {
  throw new Error('同步错误!');
}

try {
  normalFunction();
} catch (error) {
  console.error('捕获到错误:', error); // 这里能捕获到
}
  • async 函数:使用 try...catch 可以捕获同步错误由 await 导致的 Promise 拒绝(rejection)。
// asyncReject() 模拟返回一个被 reject 的 Promise
async function asyncReject() {
  const promise: Promise<number> = new Promise((resolve: Function, reject: Function) => {
    setTimeout(() => {
      reject(new Error('错误!'));
    }, 1000);
  })
  return promise;
}

async function asyncFunction() {
  try {
    const data = await asyncReject(); // 当Promise 被 reject,会被 catch 住!
  } catch (error) {
    console.error('失败:', error); // 既能捕获 asyncReject 的失败,也能捕获函数体内异常
  }
}

asyncFunction();

如果你不用 try...catch,也可以在调用时使用 .catch() 来捕获错误:

async function asyncFunction_notry() {
  const data = await asyncReject(); // 当Promise 被 reject,上层可捕获
}
asyncFunction_notry().then(()=>{
        console.error("then");
      }).catch(()=>{
        console.error("catch");
      }).finally(()=>{
        console.error("finally");
      })

总结与如何选择

普通函数

async 函数

使用场景

执行简单的、同步的计算和操作。

执行需要等待的操作,如:

1. 网络请求 (fetchaxios)

2. 文件读写

3. 定时器 (setTimeout 包装成 Promise)

4. 任何返回 Promise 的API

关键决策点

你的操作是立即完成的吗?

你的操作需要等待一段时间(如I/O)吗?

返回值

Promise实例

总结来说:加 async 的函数和普通函数最根本的区别在于返回值类型和内部是否允许使用 await。这导致了它们在执行流程和控制方式上的差异。当需要在函数内部进行异步操作(尤其是需要等待 Promise 的结果)时,就使用 async 函数并结合 await。否则,使用普通函数即可。

最后再打下广告,对本书感兴趣的同学可以点击以下链接进行购买,或者了解我的班级参加 班级共同学习,点击链接可进入(华为官方活动)

【鸿蒙生态共建】一文两表三实例讲清async函数与普通函数的区别-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利_执行流程_06