【安卓转HarmonyOS 鸿蒙Next开发之——同步与异步的思维转换】

发布于 1周前 作者 htzhanglong 来自 鸿蒙OS

【安卓转HarmonyOS 鸿蒙Next开发之——同步与异步的思维转换】

其实这也是多数习惯了同步编程的同学共同的思维转换问题,早期的编程开发语言基本都是从例程顺序开发起步,加上分支控制等形成面向过程的开发思路;之后以消息为中心有了面向对象编程,但是代码还是以同步执行为主导;异步主要用在多线程之类的代码上;而现在ArkTS/TypeScript的代码有了较大的变化,异步执行成为主流;这便要求编程思路的转换;但同步执行对于习惯了事件一步步来的思维方式还是更让开发者有习惯依赖;await 方式是强制将异步执行变成同步效果;说白了是在异步的底层上包个同步的皮:)

打个比方,在一个工厂里,工头带着生产指令在各个环节转悠,一个个下达生产指令;同步方式类似工头跑到每个工位,盯着工位上的人完成一个具体的生产操作,完成后再走向下一个工位;整个流程是单线进展的,一个工位不完成不进入下一个工位,工头某个时刻一定要盯着某个工位,哪怕工位再忙他也不能走开,从流程上讲是清晰的,逻辑也较简单,但从效率上讲是不合理的,尤其是当某个工位耗时较长时,工头的时间有浪费,不能处理其它事情,甚至如果工位出故障了,整个流程就卡住了;而异步方式呢,工头给某个工位下达生产命令后就走开了,继续处理别的事,但留下一句话:干完了找我汇报。这样工头在每个工位上花的时间就只是给出指令即可,然后就可处理别的事,效率自然是高于同步的。当然这也不是没有影响的,因为异步思路,流水线式组织生产的模式就得相应变化。反映到开发上,异步对于UI的性能是有很大的好处的,UI一般不会因为某个处理的阻塞导致UI不响应,因为所有具体事务处理只是交待好要做什么就返回了,无论具体处理耗时与否,不会影响UI线程;发出的处理指令由后台排除进行处理;处理完了由回调函数返回调用方,调用方在回调中准备好后续处理逻辑,一切行云流水,基本不会卡顿。这里如果带着同步的思路使用异步的方法是很容易出问题的;因为异步方法的结果通常不是即时获得的,如果以同步的思维可能会以为发出异步调用后一一步即可对结果进行处理,如赋值保存之类的,但实际上立即处理结果是得不到正确结果的;后果就是程序看着正确,执行结果却出人意料。后面会稍作解释。

想明白了两种编程思路的差异,在代码上就要相应地作出改变;查看API文档最常见的就是发现很多接口方法都有两种格式,一种是在参数中有callback,一种是返回Promise<T>;没用过之前很是迷惑;用过一段时间后才找到感觉:)有样学样,异步方法编程中,最直观的就是自定义的方法中学习这两种格式,要么带个 callback: Function 作为方法的入参,要么也返回个Promise<type>;这样调用方也就可以以异步方式使用这些方法了;特别是对于要调用API中异步方法的自定义方法,这应该是比较正统的操作。两种方式的区别是:

1、callback 入参方式:代码上在调用方是一层层的嵌套,看着挺吓人,但逻辑上基本很简单——调用异步方法,返回值可以将错误消息和正确结果都返回,判断有错误否;有则打日志,无则继续正常处理;其中的callback 参数是可一层层传递的,最后在某个环节由其选择要返回给调用方的数据;可以尝试一下如何将callback作为参数一级级传递给方法内部要调用的另一异步方法;没用过的一开始真挺蒙的:)不过一旦习惯后思路就可以和真实异步流程对齐;函数参数中不只可传递数据,还可传递方法使得代码的灵活性有更大提高;

2、Promise<T> 返回方式:此方式在调用异步方法后,可在后续跟上.then(()=>{…}) 形成链式回调的代码形式;其实内在和callback是异曲同工;都是回调异步处理结果,只是代码形式上的变化,看起来有流水线的感觉;then()中的 ()=>{…}即和callback回调是一回事,在代码上不是一层层嵌套,转换成了类似 类的成员方法的链式调用形式;不过注意的是每个then()的回调中可再次调用另一Promise形式的异步方法,然后再继续下一个.then();如果callback方式是俄罗斯套娃,那Promise<T>就是类似链子,一环接一环。

举个callback的例子(ArkTS/Stage/API9),更容易理解前面说的一堆:(Promise的示例可在API文档中找,理解了思路一看就明白了)

以MD5哈希为例,API9中处理消息摘要需要三步:创建md对象;添加要处理的数据;生成最终消息摘要;其中第一步不是异步方法,后两步皆为异步方法;封装一个自定义方法如下:

* 注意其中的最后一个callback参数,以及回调使用的方式和时机;处理过程中,创建好md对象后,先调用md.update(),在其回调中嵌套md.digest(),然后在digest()的回调中调用callback回调参数将结果返回给整个自定义方法的调用方。本例中对于每个异步方法调用可能产生的出错,都在此自定义方法中进行了日志记录,无需返回给整个方法的调用方,返回的只有成功的结果,如果不成功,调用方不会收到任何回调;调试看日志即可了解出错在哪一步。

export function mdProcess(str: string, callback: Function) {
  let md;
  try {
    md = cryptoFramework.createMd(‘MD5’);
  } catch (error) {
    Logger.error(tag, ----- createMd() err code: ${error.code}, msg: ${error.message}});
  }
  if (md) {
    md.update({data: stringToUint8Array(str)}, (err, ) => {
      if (err) {
        Logger.error(tag, ----- update err code: ${err.code}, msg: ${err.message}});
      } else {
        md.digest((err2, out) => {
          if (err2) {
            Logger.error(tag, ----- digest err code: ${err2.code}, msg: ${err2.message}});
          } else {
            let str = uint8ArrayToHexStr(out.data);
            callback(str);
          }
        })
      }
    })
  }
}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

相应的,调用方示例如下:

  mdProcess(this.msg, (result) => {
    //哈希最终结果即为result,后续处理可在此进行
  })
  //此处可执行其它与MD5哈希无关操作,但不能进行与上面调用相关的操作,否则得不到预期结果<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

注意:异步调用通常都要用上述两种回调方式之一进行后续处理;初始使用这种异步API时最常犯的错误即把异步方法当作同步方法用,直接在异步调用后面跟进结果处理,基本上都会得到错误结果,【谨记】一定要在回调中处理;这种回调异步处理是种趋势;()=>{…} 箭头函数是一种回调简化形式,比匿名方法更简洁,已在多个不同语言中使用,格式上有细微差异,如 Java 从 Java8 开始引进,不过Java中用 -> 而非 =>;

ArkTS/TS用了这种异步方法其实好处是很多的,比如耗时操作文件读写、网络访问等都是用异步实现;而原来为了不阻塞UI线程通常还要开新线程来执行,又增加了很多代码,而且增加了代码逻辑的复杂性;用了异步方法则相当于省去了启用不同线程的过程;开发者不再需要操心这类操作对UI的影响大不大,是否要开个线程之类的问题;代码逻辑可关注在业务逻辑上,之前为了不影响UI性能所增加的额外开发就减少了,感觉了异步的好处了么?

另外说一下 async/await 的使用,这是为了既享受异步方法的好处,又在代码上延用原来同步编写的方式而准备的表面同步底层异步机制;本来我以为会有阻塞影响UI性能,但看过多篇文章都说不会造成UI阻塞,这样看来await只是中断当前执行逻辑,将当前执行流挂起到等待队列,UI线程依然会继续,不造成阻塞;根据语法要求 await 只能在声明为 async 的函数中使用,因此所在函数还得是个异步,这样看来代码中最终一定有个起始的调用是得用一般异步调用的方式(上面两种形式),之后的函数内部可使用await来简化异步调用的形式;而且await只能针对Promise形式的异步调用,callback形式的调用不能使用await语法;更加详尽的具体用法可在搜索引擎中搜索 async/await 用法,有大量介绍文章说明,需要仔细体会并理解,不恰当的使用会导致奇怪的结果(实则为理解不到位的非正确预期)。

个人理解,有错勿喷:)

2 回复
挺好的帖子,对于我们由浅入深的帮助我更好的理解了callback和promise方式的异步调用

作为IT专家,对于安卓转HarmonyOS Next开发中的同步与异步思维转换问题,我有以下专业解答:

在安卓开发中,我们习惯于同步编程,代码按顺序执行,逻辑简单清晰。但在HarmonyOS Next中,异步执行成为主流,要求开发者进行思维转换。

异步编程中,代码不会等待某个操作完成就继续执行,提高了执行效率。例如,在工厂生产模式中,同步方式类似工头盯着每个工位完成操作,效率较低;而异步方式则允许工头在给出指令后继续处理其他事情,效率更高。

在HarmonyOS Next中,异步编程主要通过Promise和async/await实现。Promise对象用于处理异步操作,提供状态机制来管理异步操作的不同阶段,并允许注册回调函数以处理成功或失败的结果。async/await则是Promise的语法糖,使得异步代码看起来更像同步代码,更易读易懂。

对于安卓开发者来说,需要适应这种异步编程方式,学会在回调中处理结果,避免将异步方法当作同步方法使用。同时,也需要掌握Promise和async/await的用法,以便在HarmonyOS Next开发中高效地进行异步编程。

总之,同步与异步的思维转换是安卓转HarmonyOS Next开发中的重要一环。希望以上解答能帮助你更好地理解这个问题。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部