DEV Community

HarmonyOS
HarmonyOS

Posted on

Fixing KV Store “undefined” due to Callback Timing: Migrating to Promise + async-await on HarmonyOS

Read the original article:Fixing KV Store “undefined” due to Callback Timing: Migrating to Promise + async-await on HarmonyOS

Fixing KV Store “undefined” due to Callback Timing: Migrating to Promise + async/await on HarmonyOS

Problem Description

A custom database manager wraps distributed Key–Value (KV) operations. In aboutToAppear, the page fetches data from network and then writes/reads via the manager. Despite using await at call sites, write/read fail because the underlying kvStore is still undefined.

Background Knowledge

Troubleshooting Process

  1. Reviewed page flow: createMyKVManagergetMyKVStoreputget (each preceded by await).
  2. Inspected manager code: getMyKVStore() was implemented with callback form (getKVStore(..., (err, store) => {...})).
  3. Observed logs: “put/get” executed before the callback set this.kvStoreundefined.

Analysis Conclusion

await does not wait for callback-based AsyncCallback APIs. Because the manager used the callback form, the page-level await sequence did not enforce the required ordering, causing kvStore to be undefined during put/get.

Solution

Use the Promise-based API and pair it with async/await end-to-end:

  • Change getMyKVStore() to the Promise form and await it.
  • Do the same for put/get operations (Promise + await).
  • This guarantees that initialization finishes before subsequent reads/writes.
import distributedKVStore from '@ohos.data.distributedKVStore'
import { BusinessError } from '@kit.BasicServicesKit'
import { common } from '@kit.AbilityKit'

type KVManager = distributedKVStore.KVManager
type SingleKVStore = distributedKVStore.SingleKVStore
type Options = distributedKVStore.Options

function getUIContext(component: ESObject): common.UIAbilityContext {
  return getContext(component) as common.UIAbilityContext
}

class MyKV {
  private kvManager: KVManager | undefined
  private kvStore: SingleKVStore | undefined
  private readonly storeId: string = 'demo_store'

  async createMyKVManager(ctx: common.UIAbilityContext): Promise<void> {
    try {
      const config: distributedKVStore.KVManagerConfig = {
        context: ctx,
        bundleName: ctx.applicationInfo.name
      }
      this.kvManager = distributedKVStore.createKVManager(config)
      console.info('====> KVManager created')
    } catch (e) {
      const err = e as BusinessError
      console.error(`createMyKVManager failed: code=${err.code}, msg=${err.message}`)
      throw new Error(`KVManager creation failed: ${err.message}`)
    }
  }

  async getMyKVStore(): Promise<void> {
    if (!this.kvManager) {
      throw new Error('KVManager not ready')
    }
    const options: Options = {
      createIfMissing: true,
      encrypt: false,
      backup: false,
      autoSync: true,
      kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
      securityLevel: distributedKVStore.SecurityLevel.S2
    }
    try {
      this.kvStore = await this.kvManager.getKVStore<SingleKVStore>(this.storeId, options)
      console.info('====> Succeeded in getting KVStore')
    } catch (e) {
      const err = e as BusinessError
      console.error(`getMyKVStore failed: code=${err.code}, msg=${err.message}`)
      throw new Error(`KVStore creation failed: ${err.message}`)
    }
  }

  async putData(key: string, value: string): Promise<void> {
    if (!this.kvStore) {
      throw new Error('KVStore not ready')
    }
    try {
      await this.kvStore.put(key, value)
      console.info('====> PutData: Succeeded')
    } catch (e) {
      const err = e as BusinessError
      console.error(`putData failed: code=${err.code}, msg=${err.message}`)
      throw new Error(`Put data failed: ${err.message}`)
    }
  }

  async getData(key: string): Promise<string> {
    if (!this.kvStore) {
      throw new Error('KVStore not ready')
    }
    try {
      const v = await this.kvStore.get(key)
      const res: string = typeof v === 'string' ? v : JSON.stringify(v)
      console.info(`====> GetData: ${res}`)
      return res
    } catch (e) {
      const err = e as BusinessError
      console.error(`getData failed: code=${err.code}, msg=${err.message}`)
      throw new Error(`Get data failed: ${err.message}`)
    }
  }
}

@Entry
@Component
struct Index {
  @State private log: string = ''
  @State private valueFromStore: string = ''
  private readonly dataKey: string = 'data'
  private readonly manager: MyKV = new MyKV()

  private setLog(m: string): void {
    this.log = m
  }

  private async simulateNetworkFetch(): Promise<string> {
    return new Promise<string>((resolve) => {
      setTimeout(() => resolve('This is test data'), 150)
    })
  }

  async aboutToAppear(): Promise<void> {
    const ctx: common.UIAbilityContext = getUIContext(this)
    this.setLog('Starting KV init & data flow...')

    try {
      await this.manager.createMyKVManager(ctx)
      await this.manager.getMyKVStore()

      const data: string = await this.simulateNetworkFetch()
      await this.manager.putData(this.dataKey, data)
      const readBack: string = await this.manager.getData(this.dataKey)
      this.valueFromStore = readBack
      this.setLog('Flow OK: data written & read back.')
    } catch (err) {
      const error = err as Error
      this.setLog(`Error: ${error.message}`)
      console.error('Flow failed:', error.message)
    }
  }

  build() {
    Column({ space: 10 }) {
      Text('Distributed KV (Promise + async/await)')
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .margin({top: "35vp"})

      Text(`Last value: ${this.valueFromStore}`)
        .fontSize(14)
        .maxLines(2)

      Text(`Log: ${this.log}`)
        .fontSize(12)
        .maxLines(3)
        .fontColor('#666666')

      Button('Re-run flow')
        .onClick(async () => {
          await this.aboutToAppear()
        })
    }
    .width('100%')
    .height('100%')
    .padding(12)
  }
}
Enter fullscreen mode Exit fullscreen mode

Verification Result

After migrating to Promise + async/await, logs show that KVStore is obtained before put/get; the data is written and read back successfully.

asd.gif

Related Documents or Links

Written by Bunyamin Eymen Alagoz

Top comments (0)