Arkts初识

空安全

默认情况下,ArkTS中的所有类型都是不可为空的,因此类型的值不能为空。这类似于TypeScript的严格空值检查模式(strictNullChecks),但规则更严格。

在下面的示例中,所有行都会导致编译时错误:

在这里插入图片描述

可以为空值的变量定义为联合类型T | null。

在这里插入图片描述

非空断言运算符

后缀运算符“ !”可用于断言其操作数为非空。

应用于可空类型的值时,它的编译时类型变为非空类型。例如,类型将从T | null更改为T:

在这里插入图片描述

空值合并运算符

空值合并二元运算符??用于检查左侧表达式的求值是否等于null或者undefined。如果是,则表达式的结果为右侧表达式;否则,结果为左侧表达式。

换句话说,a ?? b等价于三元运算符(a != null && a != undefined) ? a : b。

在以下示例中,getNick方法如果设置了昵称,则返回昵称;否则,返回空字符串:
在这里插入图片描述

可选链

在访问对象属性时,如果该属性是undefined或者null,可选链运算符会返回undefined。

说明:getSpouseNick的返回类型必须为string | null | undefined,因为该方法可能返回null或者undefined。

可选链可以任意长,可以包含任意数量的?.运算符。

在以下示例中,如果一个Person的实例有不为空的spouse属性,且spouse有不为空的nick属性,则输出spouse.nick。否则,输出undefined:

在这里插入图片描述

在这里插入图片描述

//非空断言
export class A{
  a:number=0
}
export function fc(aa:A|null){
  // aa?.a=9
  // console.log(`${aa!.a}`)
  console.log(`${aa?.a}`)//可选链
}

//空值合并运算符
export function hb(str:string|null){
  // 三元运算符
  // return (str!=null && str!=undefined)?str:'asd'
  return str ?? 'asd'
}
import { A, fc, hb } from '../arkts/Null';
import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct NullPage {
  @State message: string = 'Hello World';

  build() {
    RelativeContainer() {
      Text(this.message)
        .id('NullPageHelloWorld')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: '__container__', align: VerticalAlign.Center },
          middle: { anchor: '__container__', align: HorizontalAlign.Center }
        })
        .onClick(()=>{
          let a:A=new A();
          fc(a)
          fc(null)
          // let str=hb(null)
          let str=hb('张三')
          promptAction.showToast({message:str})
        })
    }
    .height('100%')
    .width('100%')
  }
}

模块

  • 程序可划分为多组编译单元或模块。
  • 每个模块都有其自己的作用域,即,在模块中创建的任何声明(变量、函数、类等)在该模块之外都不可见,除非它们被显式导出。
  • 与此相对,从另一个模块导出的变量、函数、类、接口等必须首先导入到模块中。
  • 导出
  • 可以使用关键字export导出顶层的声明。
  • 未导出的声明名称被视为私有名称,只能在声明该名称的模块中使用。
  • 注意:通过export方式导出,在导入时要加{}。
  • 在这里插入图片描述

静态导入

导入声明用于导入从其他模块导出的实体,并在当前模块中提供其绑定。导入声明由两部分组成:

  1. 导入路径,用于指定导入的模块;
  2. 导入绑定,用于定义导入的模块中的可用实体集和使用形式(限定或不限定使用)。
    1. 导入绑定* as A表示绑定名称“A”,通过A.name可访问从导入路径指定的模块导出的所有实体:

      1. 在这里插入图片描述
    2. 导入绑定{ ident1, …, identN }表示将导出的实体与指定名称绑定,该名称可以用作简单名称:

      1. 在这里插入图片描述
    3. 如果标识符列表定义了ident as alias,则实体ident将绑定在名称alias下:

      1. 在这里插入图片描述

动态导入

  • 应用开发的有些场景中,如果希望根据条件导入模块或者按需导入模块,可以使用动态导入代替静态导入。
  • import()语法通常称为动态导入dynamic import,是一种类似函数的表达式,用来动态导入模块。以这种方式调用,将返回一个promise。
  • 如下例所示,import(modulePath)可以加载模块并返回一个promise,该promise resolve为一个包含其所有导出的模块对象。该表达式可以在代码中的任意位置调用。

在这里插入图片描述

  • 如果在异步函数中,可以使用let module = await import(modulePath)。

在这里插入图片描述

  • 可以像下面这样进行动态导入:

在这里插入图片描述

导入HarmonyOS SDK的开放能力

HarmonyOS SDK提供的开放能力(接口)也需要在导入声明后使用。可直接导入接口模块来使用该模块内的所有接口能力

在这里插入图片描述

顶层语句

顶层语句是指在模块的最外层直接编写的语句,这些语句不被包裹在任何函数、类、块级作用域中。顶层语句包括变量声明、函数声明、表达式等。

从HarmonyOS NEXT Developer Preview 1版本开始引入Kit概念。SDK对同一个Kit下的接口模块进行了封装,开发者在示例代码中可通过导入Kit的方式来使用Kit所包含的接口能力。其中,Kit封装的接口模块可查看SDK目录下Kit子目录中各Kit的定义。

通过导入Kit方式使用开放能力有三种方式:

  1. 方式一:导入Kit下单个模块的接口能力
    在这里插入图片描述

  2. 方式二:导入Kit下多个模块的接口能力。
    在这里插入图片描述

  3. 方式三:导入Kit包含的所有模块的接口能力。

    1. 其中,“module”为别名,可自定义,然后通过该名称调用模块的接口。
      在这里插入图片描述

​ 2.方式三可能会导入过多无需使用的模块,导致编译后的HAP包太大,占用过多资源,请谨慎使用。

  • 程序可划分为多组编译单元或模块。
  • 每个模块都有其自己的作用域,即,在模块中创建的任何声明(变量、函数、类等)在该模块之外都不可见,除非它们被显式导出。
  • 与此相对,从另一个模块导出的变量、函数、类、接口等必须首先导入到模块中。
  • 导出
  • 可以使用关键字export导出顶层的声明。
  • 未导出的声明名称被视为私有名称,只能在声明该名称的模块中使用。
  • 注意:通过export方式导出,在导入时要加{}。

并发

  • 并发是指在同一时间内,存在多个任务同时执行的情况。对于多核设备,这些任务可能同时在不同CPU上并行执行。对于单核设备,多个并发任务不会在同一时刻并行执行,但是CPU会在某个任务休眠或进行I/O操作等状态下切换任务,调度执行其他任务,提升CPU的资源利用率。
  • 为了提升应用的响应速度与帧率,避免耗时任务对主线程的影响,ArkTS提供了异步并发和多线程并发两种处理策略。
  • 异步并发是指异步代码在执行到一定程度后会被暂停,以便在未来某个时间点继续执行,这种情况下,同一时间只有一段代码在执行。ArkTS通过Promise和async/await提供异步并发能力,适用于单次I/O任务的开发场景。
  • 多线程并发允许在同一时间段内同时执行多段代码。在主线程继续响应用户操作和更新UI的同时,后台线程也能执行耗时操作,从而避免应用出现卡顿。ArkTS通过TaskPool和Worker提供多线程并发能力,适用于耗时任务等并发场景。
  • 并发多线程场景下,不同并发线程间需要进行数据通信,不同类别对象的传输方式存在差异,包括拷贝或内存共享等。

异步并发 (Promise和async/await)

  • Promise和async/await提供异步并发能力,是标准的JS异步语法。异步代码会被挂起并在之后继续执行,同一时间只有一段代码执行,适用于单次I/O任务的场景开发,例如一次网络请求、一次文件读写等操作。无需另外启动线程执行。
  • 异步语法是一种编程语言的特性,允许程序在执行某些操作时不必等待其完成,而是可以继续执行其他操作。
Promise
  • Promise是一种用于处理异步操作的对象,可以将异步操作转换为类似于同步操作的风格,以方便代码编写和维护。Promise提供了一个状态机制来管理异步操作的不同阶段,并提供了一些方法来注册回调函数以处理异步操作的成功或失败的结果。

  • Promise有三种状态:pending(进行中)、fulfilled(已完成)和rejected(已拒绝)。Promise对象创建后处于pending状态,并在异步操作完成后转换为fulfilled或rejected状态。

  • 最基本的用法是通过构造函数实例化一个Promise对象,同时传入一个带有两个参数的函数,通常称为executor函数。executor函数接收两个参数:resolve和reject,分别表示异步操作成功和失败时的回调函数。

  • 在这里插入图片描述

  • 上述代码中,setTimeout函数模拟了一个异步操作,并在1秒钟后随机生成一个数字。如果随机数大于0.5,则执行resolve回调函数并将随机数作为参数传递;否则执行reject回调函数并传递一个错误对象作为参数。

  • Promise对象创建后,可以使用then方法和catch方法指定fulfilled状态和rejected状态的回调函数。then方法可接受两个参数,一个处理fulfilled状态的函数,另一个处理rejected状态的函数。只传一个参数则表示当Promise对象状态变为fulfilled时,then方法会自动调用这个回调函数,并将Promise对象的结果作为参数传递给它。使用catch方法注册一个回调函数,用于处理“失败”的结果,即捕获Promise的状态改变为rejected状态或操作失败抛出的异常。

  • 在这里插入图片描述

  • 上述代码中,then方法的回调函数接收Promise对象的成功结果作为参数,并将其输出到控制台上。如果Promise对象进入rejected状态,则catch方法的回调函数接收错误对象作为参数,并将其输出到控制台上。

async/await
  • async/await是一种用于处理异步操作的Promise语法糖,使得编写异步代码变得更加简单和易读。通过使用async关键字声明一个函数为异步函数,并使用await关键字等待Promise的解析(完成或拒绝),以同步的方式编写异步操作的代码。

  • async函数是一个返回Promise对象的函数,用于表示一个异步操作。在async函数内部,可以使用await关键字等待一个Promise对象的解析,并返回其解析值。如果一个async函数抛出异常,那么该函数返回的Promise对象将被拒绝,并且异常信息会被传递给Promise对象的onRejected()方法

  • 下面是一个使用async/await的例子,其中模拟了一个以同步方式执行异步操作的场景,该操作会在3秒钟后返回一个字符串。

  • 在这里插入图片描述

  • 在上述示例代码中,使用了await关键字来等待Promise对象的解析,并将其解析值存储在result变量中。

  • 需要注意的是,由于要等待异步操作完成,因此需要将整个操作包在async函数中。除了在async函数中使用await外,还可以使用try/catch块来捕获异步操作中的异常。

  • 在这里插入图片描述

import { asy, p } from '../arkts/Asyac';

@Entry
@Component
struct AsyacPage {
  @State message: string = 'Hello World';

  build() {
    RelativeContainer() {
      Text(this.message)
        .id('AsyacPageHelloWorld')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: '__container__', align: VerticalAlign.Center },
          middle: { anchor: '__container__', align: HorizontalAlign.Center }
        })
        // .onClick(()=>{
        //   asy()
        // })
        .onClick(()=>{
          p()
            .then(data=>{
              console.log(data.toString()+'-----1')
              return p()
            })
            .then(data=>{
              console.log(data.toString()+'-----1')
              // return p()
            })
            .catch((er:string)=>{
              console.log(er.toString()+'-----1')
            })
        })
    }
    .height('100%')
    .width('100%')
  }
}
export function p():Promise<number>{
  return new Promise((r,j)=>{
    setTimeout(() => {
      let num=Math.random()
      if(num>0.5){
        r(num)
      }else{
        j('拒绝:你是个傻逼')
      }
    }, 1000);
  })
}

export async function asy() {
  console.log('开始执行异步操作');
  try{
    let num1=await p()
    console.log(num1.toString());
    let num2=await p()
    console.log(num2.toString());
  }catch(error){
    console.log(error);
  }
}

// function a():string{
//   return 123
// }
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>异步</title>
</head>

<body>
    <script>
        // console.log('任务1');
        // setTimeout(() => {
        //     console.log('任务2');
        // }, 2000);
        // console.log('任务3');
        // console.log('任务4');
        // console.log('任务5');
        // console.log('任务6');
        // console.log('任务7');

        // 生成10个随机数,每隔1秒生成一个   回调地狱
        // setTimeout(() => {
        //     console.log(Math.random() + '');
        //     setTimeout(() => {
        //         console.log(Math.random() + '');
        //         setTimeout(() => {
        //             console.log(Math.random() + '');
        //             setTimeout(() => {
        //                 console.log(Math.random() + '');
        //                 setTimeout(() => {
        //                     console.log(Math.random() + '');
        //                 }, 1000);
        //             }, 1000);
        //         }, 1000);
        //     }, 1000);
        // }, 1000);

        //resolve成功  reject失败
        let p1=new Promise((resolve,reject)=>{
            //生成随机数,小于0.5失败,大于0.5成功
            let num=Math.random()
            if(num<0.5){
                reject('小于0.5')
            }else{
                resolve('成功:'+num)
            }
        });
        //try{}catch(e){}
        //then()用来处理resolve的数据  data成功后返回的数据
        p1
        .then(data=>{
            console.log(data);
        })
        .catch(err=>{
            console.log(err);
        })
        .finally(()=>{
            console.log('最终执行');
        })

        // 生成10个随机数,每隔1秒生成一个 
        let p2=new Promise((r,j)=>{
            setTimeout(() => {
                r(Math.random())
            }, 1000);
        })
        p2
        .then(data=>{//处理成功
            console.log(data);
            //下一个要执行的异步操作
            return new Promise((r,j)=>{
            setTimeout(() => {
                r(Math.random())
            }, 1000);
        })
        },
        err=>{//处理失败
            console.log(err);
            
        }
    )
        .then(data=>{//接受的是上一个then的return的异步操作的结果
            console.log(data)
        })
        .catch(err=>{
            console.log(err);
        })


        //async函数  达到的效果是同步代码的效果


        //1.生成一个promise
        function p(){
            return new Promise((r,j)=>{
                setTimeout(() => {
                    let num=Math.random()
                    if(num>0.5){
                        r(num)
                    }else{
                        j(-1)
                    }
                }, 1000);
            })
        }
        //使用异步函数 es8
        async function asy() {
            console.log('开始执行异步操作');
            try{
                let num1=await p()
                console.log(num1);
                let num2=await p()
                console.log(num2);
            }catch(error){
                console.log(error);
                
            }
        }
        console.log('主线程任务开始');
        asy()
        // console.log('主线程任务1');
        // console.log('主线程任务2');
        // console.log('主线程任务3');
        // console.log('主线程任务4');
        // console.log('主线程任务5');
        // console.log('主线程任务6');
    </script>
</body>

</html>

(num2);
}catch(error){
console.log(error);

        }
    }
    console.log('主线程任务开始');
    asy()
    // console.log('主线程任务1');
    // console.log('主线程任务2');
    // console.log('主线程任务3');
    // console.log('主线程任务4');
    // console.log('主线程任务5');
    // console.log('主线程任务6');
</script>
```
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐