在Arkts中用ts实现更适合原生宝宝的Promise.WithResolver和LazyInit/Synchronize

开发过程中被各种async弄到破防,因此手撸了几个工具类保证代码稳定性,话不多说直接上代码,文件后缀需要改成.ts比如PromiseUtil.ts

export function Promise_withResolver<T>() {
  let resolve: (value: T) => void = undefined
  let reject: (err: Error) => void = undefined
  const promise = new Promise<T>((res, rej) => {
    resolve = res
    reject = rej
  })
  return {
    promise: promise,
    resolve: resolve,
    reject: reject
  }
}

该代码复刻js最新Promise的API,将resolver和reject从Promise中拆除,使数据的观察和触发分离,上场景
扫码组件初始化需要XComponent组件onLoad及权限判断成功后才开始扫码,扫码尺寸需要匹配XComponent宽高

private areaCallback = Promise_withResolver<Area>()
private loadCallback = Promise_withResolver<boolean>()
private permissionCallback = Promise_withResolver<boolean>()
private checkInitCallback = Promise.all([this.areaCallback.promise, this.loadCallback.promise, this.permissionCallback.promise])
build(){
          XComponent({
            id: 'componentId',
            type: 'surface',
            controller: this.mXComponentController
          })
            .onAreaChange((_, newValue) => {
              this.areaCallback.resolve(newValue)
            })
            .onLoad(() => {
              this.loadCallback.resolve(true)
            })
            .size(FullScreen)
}

aboutToAppear(): void {
    this.checkInitCallback.then((value) => {
      this.surfaceId = this.mXComponentController.getXComponentSurfaceId();
      this.cameraHeight = value[0].height as number
      this.cameraWidth = value[0].width as number
      if (value[2]) {
        this.vm.changeState(SCAN)
      } else {
        this.vm.changeState(NO_PERMISSION)
      }
    })
}

在进行页面的初始化时有很多场景触发接口去请求默认数据,相同的请求经常重复发送,所以想起了Synchronize原理,ts中实现如下

export function LazyInit<T>(block: () => Promise<T>) {
  const waitQueue: Array<(value: T) => void> = []
  let init: boolean | undefined = undefined
  let value: T
  return () => {
    if (init === undefined) {
      return new Promise<T>((res, rej) => {
        init = false
        block().then((value) => {
          init = true
          res(value)
          while (waitQueue.length != 0) {
            waitQueue.pop()?.(value)
          }
        })
      })
    } else if (init) {
      return Promise.resolve(value)
    } else {
      const resolver = Promise_withResolver<T>()
      waitQueue.push(resolver.resolve)
      return resolver.promise
    }
  }
}

直接上场景

  waitAndReturn() : Promise<string>{
    return new Promise((res) => {
      setTimeout(res, 5000, "123")
    })
  }

  private data = LazyInit(async () => {
    return await this.waitAndReturn()
  })
    Logger.debug("gscgsc1", `start`)
    this.data().then((value : string) => {
      Logger.debug("gscgsc1", `${value}`)
    })
    this.data().then((value : string) => {
      Logger.debug("gscgsc2", `${value}`)
    })
    this.data().then((value : string) => {
      Logger.debug("gscgsc3", `${value}`)
    })
    this.data().then((value : string) => {
      Logger.debug("gscgsc4", `${value}`)
    })
    this.data().then((value : string) => {
      Logger.debug("gscgsc5", `${value}`)
    })

运行截图如下


上面的LazyInit返回的是Promise,有时我们需要在全局变量初始化时进行懒加载声明,类似kotlin的by lazy,将上面的功能简化一下如下

export function byLazy<T>(block: () => T): () => T {
  let value: T | undefined
  return () => {
    if (!value) {
      value = block()
    }
    return value
  }
}

上才艺

  private n = byLazy(() => {
    Logger.debug("gscgsc", `math random`)
    return Math.random()
  })
    Logger.debug("gscgsc", 'start')
    Logger.debug("gscgsc", `${this.n()}`)
    Logger.debug("gscgsc", `${this.n()}`)
    Logger.debug("gscgsc", `${this.n()}`)
    Logger.debug("gscgsc", 'end')

日志如下


破费科特
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容