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
- Reviewed page flow:
createMyKVManager→getMyKVStore→put→get(each preceded byawait). - Inspected manager code:
getMyKVStore()was implemented with callback form (getKVStore(..., (err, store) => {...})). - Observed logs: “put/get” executed before the callback set
this.kvStore→undefined.
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 andawaitit. - Do the same for
put/getoperations (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)
}
}
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.

Top comments (0)