HarmonyOS Next IM Practical Combat: Handling Logical Error Bugs Caused by the Lack of await in Asynchronous Operations

Background Introduction

During the development of IMSDK, the logic is extremely intricate. Take the message retrieval logic as an example: first, retrieve the hot message list by sequence number, then process the messages by iterating throug…


This content originally appeared on DEV Community and was authored by kouwei qing

Background Introduction

During the development of IMSDK, the logic is extremely intricate. Take the message retrieval logic as an example: first, retrieve the hot message list by sequence number, then process the messages by iterating through their contents and handling different message types accordingly. Insert the final messages into the message table. If a message initiates a new conversation, insert the conversation information into the conversation table, user information into the user table and user member table, etc. Finally, notify the UI to update with new messages. Many network requests and database operations are asynchronous. In this entire process, numerous asynchronous operations must complete before the next step can proceed. Often, developers inadvertently forget to use await, leading to inexplicable logical bugs.

When such bugs occur, it is challenging to pinpoint the root cause based solely on the symptoms. Developers must reexamine the logic to check for missing await statements. This approach is not only inefficient but also prone to oversight. Is there a better solution?

Enter Code Linter

This is where Code Linter comes in handy. It scans your code to help identify potential issues. Run Code Linter before each code submission to check for possible risks.

Using Code Linter

Create a code-linter.json file in the root directory of your project with the following configuration format:

{  
  "files": [  
    "**/*.ets"  
  ],  
  "ignore": [  
    "**/src/ohosTest/**/*",  
    "**/src/test/**/*",  
    "**/src/mock/**/*",  
    "**/node_modules/**/*",  
    "**/oh_modules/**/*",  
    "**/build/**/*",  
    "**/.preview/**/*"  
  ],  
  "ruleSet": [  
    "plugin:@performance/recommended",  
    "plugin:@typescript-eslint/recommended"  
  ],  
  "rules": {  
    "@typescript-eslint/return-await": "error",  
    "@typescript-eslint/require-await": "error",  
    "@typescript-eslint/await-thenable": "error",  
    "@typescript-eslint/promise-function-async": "error"  
  }  
}

This configuration has three main sections:

  • files: Specifies which files to inspect (typically all .ets files).
  • ignore: Lists files to exclude (e.g., build artifacts, test files).
  • ruleSet: Aggregates rule collections (performance recommendations and general rules in this example).
  • rules: Defines specific linting rules.

After configuration, right-click on the module to be inspected in your IDE, then select Code Linter > Full Linter:

The inspection results will appear in the Code Linter output:

Modify the non-compliant code based on these results.

Introduction to Asynchronous Rules

We use three asynchronous-specific Code Linter rules:

promise-function-async

The @typescript-eslint/promise-function-async rule mandates that any function or method returning a Promise must be marked as async. This ensures that functions either:

  • Return a rejected promise, or
  • Throw an Error object.

In contrast, non-async functions returning Promise can technically do both, complicating error handling. This rule simplifies code by eliminating the need to handle both cases.

Key benefits of promise-function-async:

  1. Improves code clarity: Explicitly marks Promise-returning functions.
  2. Prevents common errors: Avoids overlooked asynchronous operations.
  3. Standardizes code style: Ensures consistent declaration of Promise-returning functions.
  4. Simplifies error handling: Leverages async function's automatic error propagation.
  5. Enhances type safety: Validates asynchronous behavior through TypeScript's type system.

Correct usage examples (all Promise-returning functions are marked async):

export const arrowFunctionReturnsPromise = async () => Promise.resolve('value');

export async function functionReturnsPromise() {
  return Promise.resolve('value');
}

// Explicit return type including non-Promise allows non-async function
export function functionReturnsUnionWithPromiseExplicitly(
  p: boolean
): string | Promise<string> {
  return p ? 'value' : Promise.resolve('value');
}

export async function functionReturnsUnionWithPromiseImplicitly(p: boolean) {
  return p ? 'value' : Promise.resolve('value');
}

Non-compliant examples:

export const arrowFunctionReturnsPromise = () => Promise.resolve('value');

export function functionReturnsPromise() {
  return Promise.resolve('value');
}

export function functionReturnsUnionWithPromiseImplicitly(p: boolean) {
  return p ? 'value' : Promise.resolve('value');
}

Full documentation: Promise Function Async Rule

await-thenable

The @typescript-eslint/await-thenable rule prohibits using the await keyword on non-"Thenable" values. A "Thenable" object has a .then() method (e.g., Promise).

Correct usage:

async function test() {
  await Promise.resolve('value');
}

export { test };

Non-compliant code:

async function test() {
  await 'value'; // Error: 'value' is not a Thenable
}

export { test };

Full documentation: Await Thenable Rule

require-await

The @typescript-eslint/require-await rule is critical. It mandates that async functions must contain at least one await expression.

Key objectives:

  1. Prevent misuse: Avoid await on non-asynchronous values, which is often a mistake.
  2. Improve clarity: Ensure await is used only for genuine asynchronous operations.
  3. Optimize performance: Eliminate unnecessary asynchronous overhead (microtask queue).
  4. Enhance type safety: Detect invalid await usage via TypeScript types.

Example of missing await:

async function doSomething(): Promise<void> {
  return Promise.resolve();
}

export async function foo() {
  doSomething(); // Linter error: Missing `await`
}

The linter flags foo() for missing await, helping uncover potential bugs.

Full documentation: Require Await Rule

return-await

The @typescript-eslint/return-await rule dictates when to use await in return statements of async functions.

In async functions, both return await promise; and return promise; are valid, but return await offers advantages:

  • Improves stack trace readability.
  • Captures promise rejections in try...catch blocks.
  • Has negligible performance impact.

This rule enforces consistent handling of returned promises with four configurations:

  • never: Prohibit await on returned promises.
  • error-handling-correctness-only: Require await in error-handling contexts (e.g., try/catch blocks).
  • in-try-catch: Require await in error-handling; prohibit elsewhere.
  • always: Always use await when returning promises.

The rule distinguishes between "normal contexts" and "error-handling contexts":

  • Error-handling contexts: Use return await to ensure catch/finally blocks execute correctly (e.g., inside try/catch).
  • Normal contexts: Style preference; direct return promise is acceptable.
Option Normal Context (Style) Error-Handling Context (Bug Prevention) Recommended?
always return await return await ✅ Yes
in-try-catch return promise return await ✅ Yes
error-handling-only No preference return await 🟡 Okay
never return promise return promise (⚠️ Risky) ❌ Deprecated

Example requiring return await in try...catch:

export async function validInTryCatch1() {
  try {
    return Promise.resolve('try'); // Linter error: Missing `await`
  } catch (e) {
    return Promise.resolve('catch'); // Linter error: Missing `await`
  }
}

Best practices:

  1. Use "in-try-catch" for a balanced approach.
  2. Always use return await inside try-catch blocks.
  3. Return promises directly elsewhere.
  4. Avoid redundant await where error handling isn't needed.

Full documentation: Return Await Rule

no-floating-promises

The @typescript-eslint/no-floating-promises rule ensures proper handling of Promise expressions. A "floating promise" is one created without error handling, leading to silent failures, incorrect execution order, or uncaught rejections.

The rule flags unhandled Promise expressions unless they are:

  • Handled with .then(onFulfilled, onRejected).
  • Caught with .catch().
  • Awaited with await.
  • Returned from a function.
  • Explicitly ignored with void.

It also detects unhandled Promises in arrays. Use Promise combinators like Promise.all() to handle them collectively.

Examples of unhandled promises:

export async function bar() {
  const promise = new Promise<string>(resolve => {
    resolve('value');
    return 'finish';
  });
  promise; // Linter error: Unhandled promise
  Promise.reject('value').catch(); // Linter error: Empty catch
  await Promise.reject('value').finally(); // Linter error: No catch
  ['1', '2', '3'].map(async x => x + '1'); // Linter error: Unhandled Promise array
}

Key benefits:

  1. Prevent silent failures: Ensure Promise rejections aren't ignored.
  2. Improve reliability: Force explicit error handling.
  3. Simplify debugging: Trace all asynchronous errors.
  4. Enhance maintainability: Clarify asynchronous data flow.

Full documentation: No Floating Promises Rule

no-misused-promises

The @typescript-eslint/no-misused-promises rule prevents Promise misuse in non-asynchronous contexts, ensuring Promises are used only where appropriate.

Core objectives:

  1. Block conditional misuse: Flag Promises used directly in conditions (e.g., if (promise)).
  2. Prevent logical operator misuse: Prohibit Promises in &&, ||, ?? operations.
  3. Ensure correct callbacks: Prevent async callbacks in synchronous contexts (e.g., forEach).
  4. Protect void return types: Block Promises in functions expected to return void (e.g., event handlers).

Problem scenarios and fixes:

Scenario 1: Conditional misuse

// ❌ Error: Using Promise directly in condition
if (fetchUserData()) { 
  // Always executes (Promise is always truthy)
  console.log('Fetching user data...');
}

// ✅ Correct: Resolve Promise first
async function checkUser() {
  const userData = await fetchUserData();
  if (userData) {
    console.log('User data loaded');
  }
}

Scenario 2: Logical operator misuse

// ❌ Error: Using Promise in logical operator
const config = loadConfig() || defaultConfig; // Always uses loadConfig()

// ✅ Correct: Resolve Promise before comparison
async function getConfig() {
  const loadedConfig = await loadConfig();
  return loadedConfig ?? defaultConfig;
}

Scenario 3: Callback misuse

// ❌ Error: Async callback in array method
[1, 2, 3].forEach(async (id) => {
  await deleteRecord(id); // Unintended concurrency
});

// ✅ Correct: Sequential processing with for...of
async function deleteRecords() {
  for (const id of [1, 2, 3]) {
    await deleteRecord(id);
  }
}

// ✅ Correct: Parallel processing with Promise.all
await Promise.all([1, 2, 3].map(id => deleteRecord(id)));

Scenario 4: Void return type misuse

// ❌ Error: Returning Promise from event handler
const button = document.getElementById('submit');
button.addEventListener('click', () => {
  return submitForm(); // Event handlers should return void
});

// ✅ Correct: Explicitly ignore Promise
button.addEventListener('click', () => {
  void submitForm(); // Indicate Promise is intentionally unhandled
});

// ✅ Correct: Handle errors with async/await
button.addEventListener('click', async () => {
  try {
    await submitForm();
  } catch (error) {
    showError(error);
  }
});

Full documentation: No Misused Promises Rule

Conclusion

This article addressed issues caused by improper asynchronous operations in HarmonyOS development. By leveraging five specific Code Linter rules, we can effectively mitigate problems related to missing await keywords and other asynchronous pitfalls.


This content originally appeared on DEV Community and was authored by kouwei qing


Print Share Comment Cite Upload Translate Updates
APA

kouwei qing | Sciencx (2025-06-28T08:33:59+00:00) HarmonyOS Next IM Practical Combat: Handling Logical Error Bugs Caused by the Lack of await in Asynchronous Operations. Retrieved from https://www.scien.cx/2025/06/28/harmonyos-next-im-practical-combat-handling-logical-error-bugs-caused-by-the-lack-of-await-in-asynchronous-operations/

MLA
" » HarmonyOS Next IM Practical Combat: Handling Logical Error Bugs Caused by the Lack of await in Asynchronous Operations." kouwei qing | Sciencx - Saturday June 28, 2025, https://www.scien.cx/2025/06/28/harmonyos-next-im-practical-combat-handling-logical-error-bugs-caused-by-the-lack-of-await-in-asynchronous-operations/
HARVARD
kouwei qing | Sciencx Saturday June 28, 2025 » HarmonyOS Next IM Practical Combat: Handling Logical Error Bugs Caused by the Lack of await in Asynchronous Operations., viewed ,<https://www.scien.cx/2025/06/28/harmonyos-next-im-practical-combat-handling-logical-error-bugs-caused-by-the-lack-of-await-in-asynchronous-operations/>
VANCOUVER
kouwei qing | Sciencx - » HarmonyOS Next IM Practical Combat: Handling Logical Error Bugs Caused by the Lack of await in Asynchronous Operations. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/06/28/harmonyos-next-im-practical-combat-handling-logical-error-bugs-caused-by-the-lack-of-await-in-asynchronous-operations/
CHICAGO
" » HarmonyOS Next IM Practical Combat: Handling Logical Error Bugs Caused by the Lack of await in Asynchronous Operations." kouwei qing | Sciencx - Accessed . https://www.scien.cx/2025/06/28/harmonyos-next-im-practical-combat-handling-logical-error-bugs-caused-by-the-lack-of-await-in-asynchronous-operations/
IEEE
" » HarmonyOS Next IM Practical Combat: Handling Logical Error Bugs Caused by the Lack of await in Asynchronous Operations." kouwei qing | Sciencx [Online]. Available: https://www.scien.cx/2025/06/28/harmonyos-next-im-practical-combat-handling-logical-error-bugs-caused-by-the-lack-of-await-in-asynchronous-operations/. [Accessed: ]
rf:citation
» HarmonyOS Next IM Practical Combat: Handling Logical Error Bugs Caused by the Lack of await in Asynchronous Operations | kouwei qing | Sciencx | https://www.scien.cx/2025/06/28/harmonyos-next-im-practical-combat-handling-logical-error-bugs-caused-by-the-lack-of-await-in-asynchronous-operations/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.