【前言】
在开发鸿蒙代码时,经常会用到async的异步函数,假设函数名称是A,如果A函数里面有个异步操作很耗时的话,我们可能会用到缓存,将耗时操作的结果保存到缓存中,如果下次再有调用A函数的话,直接返回缓存中的值。但是有时候,当缓存还没有值的时候,A函数在短时间内被调用多次,导致耗时函数会被执行多次,可能导致阻塞主线程的问题。下面首先复现下这种场景,并给出一个解决方案。
1、模拟一个耗时的异步函数,短时间内被调用多次
代码如下,其中getValue函数表示一个耗时的异步函数,我们用一个for循环执行多次,来模拟短时间被调用多次,我们可以看结果,每次调用,缓存值都还没有生成,这就导致,计算缓存值的函数会被多次执行。
javascript
@Entry
@Component
struct LockPage {
async aboutToAppear() {
let util = new LockUtil()
for (let index = 0; index < 4; index++) {
util.getValue().then((value) => {
console.log(`[lock] getValue is ${value}`)
})
}
}
build() {
RelativeContainer() {
}
}
}
export class LockUtil {
private cache = ''
public async getValue():Promise<string> {
if (this.cache) {
return this.cache
}
console.log('[lock] no cache')
// 模拟耗时操作
await new Promise<void>(resolve => setTimeout(resolve, 3000));
this.cache = '6666'
return this.cache
}
}
执行结果:

2、使用异步锁ArkTSUtils.locks.AsyncLock控制异步并发
我们这里引入系统的异步锁,来控制异步函数的并发,使得计算缓存的耗时操作只执行一次,代码如下,看日志打印,耗时函数只执行一次,其余都是从缓存中获取
javascript
public async getValueLock():Promise<string> {
if (this.cache) {
return this.cache
}
let lock:ArkTSUtils.locks.AsyncLock = ArkTSUtils.locks.AsyncLock.request('lock')
let result = await lock.lockAsync(async () => {
if (this.cache) {
return this.cache
}
console.log('[lock] no cache')
// 模拟耗时操作
await new Promise<void>(resolve => setTimeout(resolve, 3000));
this.cache = '6666'
return this.cache
})
return result
}

完整代码示例如下:
javascript
@Entry
@Component
struct LockPage {
async aboutToAppear() {
let util = new LockUtil()
for (let index = 0; index < 4; index++) {
util.getValueLock().then((value) => {
console.log(`[lock] getValue is ${value}`)
})
}
}
build() {
RelativeContainer() {
}
}
}
import { ArkTSUtils } from "@kit.ArkTS";
export class LockUtil {
private cache = ''
public async getValue():Promise<string> {
if (this.cache) {
return this.cache
}
console.log('[lock] no cache')
// 模拟耗时操作
await new Promise<void>(resolve => setTimeout(resolve, 3000));
this.cache = '6666'
return this.cache
}
public async getValueLock():Promise<string> {
if (this.cache) {
return this.cache
}
let lock:ArkTSUtils.locks.AsyncLock = ArkTSUtils.locks.AsyncLock.request('lock')
let result = await lock.lockAsync(async () => {
if (this.cache) {
return this.cache
}
console.log('[lock] no cache')
// 模拟耗时操作
await new Promise<void>(resolve => setTimeout(resolve, 3000));
this.cache = '6666'
return this.cache
})
return result
}
}