鸿蒙移动应用开发-------初始arkts
一. 什么是arkts
ArkTS是HarmonyOS优选的主力应用开发语言。
ArkTS围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展,保持了TS的基本风格,同时通过规范定义强化开发期静态检查和分析,提升程序执行稳定性和性能。TypeS
cript深受开发者的喜爱,因为它提供了一种更结构化的JavaScript编码方法。
ArkTS旨在保持TypeScript的大部分语法,为现有的TypeScript开发者实现无缝过渡,让移动开发者快速上手ArkTS。
二. arkts想比TS特性差异
ArkTS通过规范约束了TS中过于灵活而影响开发正确性或者给运行时带来不必要额外开销的特性,下面通过代码片段说明部分约束特性。

三. arkts基本知识
1.声明
arkts通过声明引入变量、常量、函数和类型
(1)变量声明
变量声明以关键字let开头的声明引入变量,该变量在程序执行期间可以具有不同的值。
// 声明了字符串变量
let str:string='hello'
//修改str变量的值为hello word
str='hello word'
(2)常量声明
常量以关键字 const 开头的声明引入只读常量,该常量只能被赋值一次。
对常量重新赋值会造成编译时错误
const str1:string='hello'
2.自动类型推断
由于arkts是一种静态类型语言,所有数据的类型都必须在编译时确定。
但是,如果一个变量或常量的声明包含了初始值,那么开发者就不需要显式指定其类型。ArkTS规范中列举了所有允许自动推断类型的场景。
// 不省略数据类型声明
let str:string='hello';
// 自动类型推断
let str1='hello word'
//出现错误示范
// str1=123 // 上面声明为字符串类型,而重新赋值为数字类型则报错
// let str // 未给初始值则报错
3.arkts中的类型
(1)number类型
ArkTS提供number和Number类型,任何整数和浮点数都可以被赋给此类型的变量。 数字字面量包括整数字面量和十进制浮点数字面量。
整数字面量包括以下类别: 由数字序列组成的十进制整数。例如:0、117、-345
// 十进制
let num1:number=123
以0x(或0X)开头的十六进制整数,可以包含数字(0-9)和字母a-f或A-F。例如:0x1123、0x00111、-0xF1A7
let num4:number=0xfac; // 十六进制
以0o(或0O)开头的八进制整数,只能包含数字(0-7)。例如:0o777
let num3:number=0o17161; // 八进制
以0b(或0B)开头的二进制整数,只能包含数字0和1。例如:0b11、0b0011、-0b11
let num2:number=0b10101; // 二进制
浮点字面量包括以下: 十进制整数,可为有符号数(即,前缀为“+”或“-”); 小数点(“.”)
// 浮点字面量
let num1:number=+1;
let num2:number=-1;
let num3:number=1.1;
小数部分(由十进制数字字符串表示) 以“e”或“E”开头的指数部分,后跟有符号(即,前缀为“+”或“-”)或无符号整数。
(2)boolean类型
boolean类型由true和false两个逻辑值组成。
console.log(3>2) //返回结果为true
console.log(5<4) //返回结果为false
(3)string类型
string代表字符序列;可以使用转义字符来表示字符。
字符串字面量由单引号(')或双引号(")之间括起来的零个或多个字符组成。
字符串字面量还有一特殊形式,是用反向单引号(``)括起来的模板字面量。
// 单引号
let str5:string='hello word';
// 双引号
let str6:string="hello word";
// 反引号
let str7:string=`hello word ${num1}`;
//转义
let str8:string='hello word\n';
(4)null类型
let a:null=null
(5)undefined类型
let b:undefined=undefined
// 或者
let b1:string;
(6)void类型
void类型用于指定函数没有返回值。 此类型只有一个值,同样是void。由于void是引用类型,因此它可以用于泛型类型参数。
function abc2():void{
// void:没有返回值,不能有返回值
}
(7)Object类型
Object类型是所有引用类型的基类型。任何值,包括基本类型的值(它们会被自动装箱),都可以直接被赋给Object类型的变量。
let ob:Object=123;
(8)array类型
array,即数组,是由可赋值给数组声明中指定的元素类型的数据组成的对象。 数组可由数组复合字面量(即用方括号括起来的零个或多个表达式的列表,其中每个表达式为数组中的一个元素)来赋值。数组的长度由数组中元素的个数来确定。数组中第一个元素的索引为0。
let arr:number[]=[1,2,3,4,5,6]
(9)enum类型
enum类型,又称枚举类型,是预先定义的一组命名值的值类型,其中命名值又称为枚举常量。
enum sex{
男,女
}
使用枚举常量时必须以枚举类型名称为前缀
let str:Sex=Sex.男
// 也可以用数字表示
let sex2:Sex=1;
(10)union类型
union类型,即联合类型,是由多个类型组合成的引用类型。联合类型包含了变量可能的所有类型。
class Dog{
play(){
console.log('狗')
}
}
export class Cat{
play(){
console.log('猫')
}
}
class Penguin{
play(){
console.log('企鹅')
}
}
// Pet 是联合类型
type Pet=Dog|Cat|Penguin
let p1:Pet=new Dog();
p1.play()
(11)Aliases类型
Aliases类型为匿名类型(数组、函数、对象字面量或联合类型)提供名称,或为已有类型提供替代名称。
type fun=(a:string)=>string
4.运算符
(1)赋值运算符
赋值运算符=,使用方式如x=y。
let a:number=1
(2)算术运算符
(3)比较运算符

(4)一元运算符
++:自增 -- :自减
(5)逻辑运算符

(6)位运算符
& 可以用来判断奇偶数
let flag = (12 & 1) == 0
console.log(`${flag}`)

(7)复合赋值运算符
复合赋值运算符列举如下:+=、-=、*=、/=、%=、<<=、>>=、>>>=、&=、|=、^=。
5.语句
(1)if语句
if语句用于需要根据逻辑条件执行不同语句的场景。当逻辑条件为真时,执行对应的一组语句,否则执行另一组语句(如果有的话)。
else部分也可能包含if语句。 条件表达式可以是任何类型。但是对于boolean以外的类型,会进行隐式类型转换:
// 如果3>2为真就执行第一条语句,否则执行语句2
if(3>2){
// 语句1
console.log('true')
}else{
// 语句2
console.log('false')
}
(2)switch语句
使用switch语句来执行与switch表达式值匹配的代码块。
如果switch表达式的值等于某个label的值,则执行相应的语句。
如果没有任何一个label值与表达式值相匹配,并且switch具有default子句,那么程序会执行default子句对应的代码块。
break语句(可选的)允许跳出switch语句并继续执行switch语句之后的语句。 如果没有break语句,则执行switch中的下一个label对应的代码块。
let a:number=3;
switch(a){
case 1:
console.log('第一名')
break
case 2:
console.log('第二名')
break
case 3:
console.log('第三名')
break
default:
console.log('没有获得名次')
};
(3)三元表达式
条件表达式由第一个表达式的布尔值来决定返回其它两个表达式中的哪一个
console.log(a>b?'a大于b':'a小于b');
判断a>b,如果a>b 执行第一条语句,否则执行第二条语句
(4)for循环语句
for语句会被重复执行,直到循环退出语句值为false。
for语句的执行流程如下:
1、 执行init表达式(如有)。此表达式通常初始化一个或多个循环计数器。
2、 计算condition。如果它为真值(转换后为true的值),则执行循环主体的语句。如果它为假值(转换后为false的值),则for循环终止。
3、 执行循环主体的语句。
4、 如果有update表达式,则执行该表达式。
5、 回到步骤2。
let sum:number=0
for(let i=0;;i<=10;i++){
sum += i
}
console.log(i)
(5)for-of语句
使用for-of语句可遍历数组或字符串
let arr:number[]=[1,2,3,4,5,6]
for (let i of arr){
console.log(`${i}`)
}
// 遍历字符串
let str:string='hello'
for (let a of str){
console.log(a)
}
(6)while语句
只要condition为真值(转换后为true的值),while语句就会执行statements语句
let a:number=0
while(a<=10){
console.log(`${a}`)
a++
}
(7)do-while语句
如果condition的值为真值(转换后为true的值),那么statements语句会重复执行
let b=0
do{
console.log(`${{b}`)
b++
}while(b<=10)
(8)break语句
使用break语句可以终止循环语句或switch。
如果break语句后带有标识符,则将控制流转移到该标识符所包含的语句块之外。
let a:number[]=[1,2,3,4,5,6]
// 当i等于3终止循环
for(let i=0;i<a;i++){
if(i==3){
break
}
console.log(i)
}
(9)continue语句
continue语句会停止当前循环迭代的执行,并将控制传递给下一个迭代。
let a:number[]=[1,2,3,4,5,6]
// 当i等于3时跳过本次循环
for(let i=0;i<a;i++){
if(i==3){
continue
}
console.log(i)
}
(10)thorw和try语句
throw语句用于抛出异常或错误:
class Err{
static add(a:number,b:number):number{
if(!b){
//抛出异常
throw new Error('除数不能为0')
}
return a/b
}
}
Err.add(1,0)
try语句用于捕获和处理异常或错误:
function tryCatch(){
let aaa:number=0
try { //有可能出现问题的代码
aaa=Err.add(1,0)
}catch (e){ //出现问题后的解决办法 e---->错误信息
console.log(e)
}
finally 异常的最终处理方案
function tryCatch(){
let aaa:number=0
try { //有可能出现问题的代码
aaa=Err.add(1,0)
}catch (e){ //出现问题后的解决办法 e---->错误信息
console.log(e)
} finally { //异常的最终处理方案
// 不管代码有没有出现异常都会执行
return aaa;
}
6.函数
函数声明引入一个函数,包含其名称、参数列表、返回类型和函数体。
function abc(){
//无参函数,并且没有返回值
}
function abc1():string{
// 除了 void和any,必须有返回值
return 'a'
}
function abc2():void{
// void:没有返回值,不能有返回值
}
(1)可选参数
function getAge(sex:string ,name?:string) {
if(name){
return `${name}${sex}`
}
return `无名${sex}`
}
可选参数的另一种形式为设置的参数默认值。如果在函数调用中这个参数被省略了,则会使用此参数的默认值作为实参。
function a(x:number,y:number=2):number{
return x*y
}
a(1,5) //返回1*5
a(2) //返回 2*2
(2)rest参数
函数的最后一个参数可以是rest参数。使用rest参数时,允许函数或方法接受任意数量的实参。
function ages(name:string,...ages:number[]){
console.log(`${name}班有${ages}.length`)
let sum=0;
for (let el of ages) {
sum+=el
}
console.log(`年龄总和:${sum},平均值${sum/ages.length}`)
return sum/ages.length
}
let avgage=ages('鸿蒙',12,23,18,21,17,19)
(3)返回类型
如果可以从函数体内推断出函数返回类型,则可在函数声明中省略标注返回类型
function abc1():string{
return 'a'
}
// 自动推导函数返回类型
function abc(){
return 'a'
}
不需要返回值的函数的返回类型可以显式指定为void或省略标注。这类函数不需要返回语句
function abc2():void{
console.log('a')
}
(4)函数的作用域
函数中定义的变量和其他实例仅可以在函数内部访问,不能从外部访问。 如果函数中定义的变量与外部作用域中已有实例同名,则函数内的局部变量定义将覆盖外部定义。
(5)函数的调用
调用函数以执行其函数体,实参值会赋值给函数的形参。
function add(a:number,b:number=3){
return a+b;
}
let a1 = add(2,6);
console.log(a1)
let a22 = add(2);
console.log(a22)
(6)函数类型
函数类型通常用于定义回调:
type fc=(str:string)=>string //对字符串进行处理并返回
function syfc(f:fc){
return f('火锅')
}
function cl(str:string){
return str+'丸子'
}
let str33=syfc(cl)
(7)箭头函数(又名lambda函数)
函数可以定义为箭头函数
箭头函数的返回类型可以省略;省略时,返回类型通过函数体推断。
表达式可以指定为箭头函数,使表达更简短
let lam1=(x:number,y:number)=>{return x+y}
lam1(1,2)
let lam2=(x:number,y:number)=>{x+y}
let lam3=(x:number,y:number)=>x+y
//{}省略,必须只有一行代码,不能写return关键字
//()无法省略
let lam4=(x:number,y:number)=>{
x+=10
return x+y
}
(8)闭包
闭包是由函数及声明该函数的环境组合而成的。该环境包含了这个闭包创建时作用域内的任何局部变量。
function abc5():()=>number{
let a=0;
let g=():number=>{return a++}
return g
}
let abc6=abc5()
abc6()
7.类
(1).创建类
类声明引入一个新类型,并定义其字段、方法和构造函数,定义类后,可以使用关键字new创建实例
// 创建一个Person类
class Person{
// 属性
name:string=''
age:number=0
play:string=''
// 构造函数
constructor(name:string.age:number){
this.name=name
this.age=age
}
// 方法
play(){
return `姓名:${name}年龄:${age}在玩${play}游戏`
}
}
// 实例类
let p:Person=new Person('张三',18,'电脑')
在类中定义属性时,必须要给初始值
构造函数:给属性初始值,初始化属性
(2)字面量创建类
class Person{
firstName:string=''
lastName:string=''
}
// 字面量创建对象
let p:Person={
firstName:'三',
lastName:'张'
}
使用字面量实例化对象,在创建类中不能有构造函数和方法
(3)静态字段
使用关键字static将字段声明为静态。静态字段属于类本身,类的所有实例共享一个静态字段要访问静态字段,需要使用类名
class Pet{
static name:string=''
age:number=''
// 静态函数,直接通过类名实现
static a(num1:number,num2:number){
Pet.name='小花'
// 在类里面,不支持this,无法直接访问非静态字段/ 函数
// this.name='aa' 错误
return num1+num2
}
}
// 静态字段使用类名进行访问
Pet.name='小猫'
(4)继承
一个类可以继承另外一个类(称为基类),基类也称为‘父类’或‘超类’,继承类继承父类的字段和方法,不能继承构造函数,如果父类有构造函数但必须调用父类的有参的构造函数,继承类也可以称为‘派生类’或‘子类’
父类访问使用关键字super可以访问父类的实例字段、实例方法和构造函数
// 创建一个宠物类,也是父类
class Pet{
name:string=''
sex:string=''
constructor(name:string,sex:string){
this.name=name
this.sex=sex
}
show(){
return `昵称:${this.name},性别:${this.sex}`
}
}
// 创建子类 只支持单继承
calss Dog extends Pet{
// 必须调用父类的有参的构造函数,使用super关键字
constructor(name:string,sex:string){
super(name,sex)
}
}
(5)方法的重写和重载
重载,在Arkts中不支持,所以就不过多讲解,主要了解一下方法重写
子类可以重写其父类中定义的方法的实现。重写的方法必须具有与原始方法相同的参数类型和相同或派生的返回类型
class Pet{
name:string=''
sex:string=''
constructor(name:string,sex:string){
this.name=name
this.sex=sex
}
show(){
return `昵称:${this.name}性别:${this.sex}`
}
}
class Dog extends Pet{
constructor(name:string,sex:string){
super(name,sex)
}
// 子类自己的属性
type:string=''
// 重写:子类重写父类的函数
// 要求:1.方法名相同 2.参数类型相同 3.返回值类型相同或是其子类
show(){
return super.show()+'品种'+this.type
}
}
(6)修饰符
1.public(共有)
public修饰的类成员(字段、方法、构造函数)在程序的任何可访问该类的地方都是可见的
2.private(私有)
private修饰的成员不能在声明该成员的类之外访问
3.protected(受保护)
protected修饰符的作用与private修饰符非常相似,不同点是protected修饰的成员允许在派生类中访问
4.getter和setter
setter和getter可用于提供对对象属性的受控访问
class Aa{
// 共有的
public a1:string=''
//受保护的:本类和子类中使用
protected a2:string=''
// 私有的
private a3:string=''
}
class Bb extends Aa{
show(){
this.a2='123'
//this.a3='ss'
}
}
let aa=new Aa()
aa.a1
let bb=new Bb()
class Pri{
private _a1: string
public set a1(value: string) {
this._a1 = value
}
public get a1(): string {
return this._a1
}
private _a2: string
public set a2(value: string) {
this._a2 = value
}
public get a2(): string {
return this._a2
}
constructor(a1: string, a2: string) {
this._a1 = a1
this._a2 = a2
}
}
let pp = new Pri('1','2')
(5)Record类型的对象字面量
泛型Record<K, V>用于将类型(键类型)的属性映射到另一个类型(值类型)。
常用对象字面量来初始化该类型的值 类型K可以是字符串类型或数值类型,而V可以是任何类型
let map:Record<string,string>={
'name':'张三',
'sex':'男',
'color':'黄'
}
let names=map['name']
class Stu{
name:string=''
age:number=0
}
let map2:Record<string,Stu>={
'张三':{name:'张三',age:18},
'张2':{name:'张2',age:18},
'张4':{name:'张4',age:18},
'张5':{name:'张5',age:18},
'张6':{name:'张6',age:18},
}
let s:Stu=map2['张三']
8.接口
接口声明引入新类型。接口是定义代码协定的常见方式。 任何一个类的实例只要实现了特定接口,就可以通过该接口实现多态。 接口通常包含属性和方法的声明
接口属性 接口属性可以是字段、getter、setter或getter和setter组合的形式。 属性字段只是getter/setter对的便捷写法
继承关键字extends
interface Stus{
// 接口中的方法,没有方法体(方法的实现)
eat():void
study():number
}
//(实现类) 实现接口,必须重写接口中的方法
class Stu1 implements Stus{
eat(): void {
console.log('学生在吃')
}
study(): number {
console.log('学生在学习')
return 100
}
}
interface Stu2{
// 接口的属性会在实现类中默认隐式生成 getter/setter 方法
name:string
sex:string
eat():void
study():number
}
class Stu3 implements Stu2{
name: string=''
sex: string=''
eat(): void {
throw new Error('Method not implemented.')
}
study(): number {
throw new Error('Method not implemented.')
}
}
let stu3=new Stu3()
stu3.name='123'
interface Inte1{
a():void
}
interface Inte2 extends Inte1{
b():void
}
class Imp1 implements Inte2{
b(): void {
throw new Error('Method not implemented.')
}
a(): void {
throw new Error('Method not implemented.')
}
}
interface A1{
a():void
}
interface B1{
b():void
}
// 实现多个接口
class C1 implements A1,B1{
b(): void {
throw new Error('Method not implemented.')
}
a(): void {
throw new Error('Method not implemented.')
}
}
9.泛型
泛型类型和函数 泛型类型和函数允许创建的代码在各种类型上运行,而不仅支持单一类型。 泛型类和接口,类和接口可以定义为泛型,将参数添加到类型定义中
常用的 T,E,K,V,? 本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。
通常情况下,T,E,K,V,?是这样约定的:
?表示不确定的 类型
T (type) 表示具体的一个类
K V (key value) 分别代表键值中的Key Value
E (element) 代表Element
?无界通配符
// 泛型 T类,K:key V:value E:element集合
// N:number ?不确定类型
class Fx<E>{
a(e:E):void{
console.log(`${e}`)
}
}
let fx=new Fx<string[]>();
fx.a(['1','2'])
// 返回数字数组的最后一个元素
function lastGet(x:number[]):number{
return x[x.length-1]
}
let aa=lastGet([1,2,3,4])
// 返回任意类型数组的最后一个元素
function getLast<T>(x:T[]):T{
return x[x.length-1]
}
let a2=getLast<string>(['1','2','as'])
10.空安全
默认情况下,Arkts中的所有类型都是不可为空的,因此类型的值不能为空
以下情况编译会报错
let x:number=null; // 编译报错
let y:string=null;// 编译报错
let z:number[]=null;// 编译报错
如果避免上述情况可以使用联合类型
let x:number|null=null
x=1
x=null
(1)非空断言
后缀运算符使用 ! 可用于断言其操作不为空
如果运行时值非空则通过,如果值为空,则发生运行时的异常
class A{
a:number=0
}
function fc(aa:A|null){
console.log(`${aa!.a}`)
}
(2)空值合并运算符
空值合并二元运算符:?? 用于检查左侧表达式的求值是否等于null或者undefined如果是,则表达式的结果为右侧表达式:否则,结果为左侧表达式。
举个例子: a ?? b 等价与三元运算符(a !=null && a != undefined ? a:b)
function hb(str:string|null){
// return (str!=null && str!=undefined)?str:'asf'
return str ?? "asd"
}
(3)可选链
在访问对象属性时,如果该属性是undefined或者null,可选链运算符会返回undefined
class A{
a:number=0
}
function fc(aa:A|null ) {
console.log(`${aa?.a}`); // 可选链
}
11.模块
(1)导出
程序可划分为多组编译单元或模块,每个模块都有其自己的作用域。
在模块中创建的任何声明(变量、函数、类等)在该模块之外都不可见,除非它们被显示导出
导出使用关键字 export 导出顶层的声明
未导出的声明被视为私有名称,只能在声明该名称的模块
export class A{
a:number=0
}
(2)导入
导入声明用于导入从其他模块导出的实体,并在当前模块中提供其绑定
导入声明由两部分组成:
1.导入路径:用于指定导入的模块;
2.导入绑定:用于定义导入的模块中的可用实体集和使用形式
import { A as a, fc, hb } from '../arkts/Nulls';
import { promptAction } from '@kit.ArkUI';
12.并发
并发是指在同一时间内,存在多个任务同时执行。对于多核设备,这些任务可能同时在不同cpu上并行执行。对于单核设备,多个并发任务不会在同一时刻并发执行。
为了提升应用的响应速度与帧率,避免了耗时任务对主线程的影响,Arkts提供了异步并发和多线程并发两种处理策略。
异步并发是指异步代码在执行到一定程度后会被暂停,以便在未来某个时间点继续执行。这种情况下,同一时间只有一段代码在执行。ArkTS通过Promise和async/await提供异步并发能力,适用于单次I/O任务的开发场景。
(1)异步并发
Promise和async/await提供异步并发能力,是标准的JS异步语法。异步代码会被挂起并在之后继续执行,同一时间只有一段代码执行。
异步语法是一种编程语言的特性,允许程序在执行某些操作时不必等待其完成,而是可以继续执行其他操作。
(2)promise
Promise是一种用于处理异步操作的对象,可以将异步操作转换为类似于同步操作的风格,以方便代码编写和维护。Promise提供了一个状态机制来管理异步操作的不同阶段,并提供了一些方法来注册回调函数以处理异步操作的成功或失败的结果。
Promise有三种状态:pending(进行中)、fulfilled(已完成)和rejected(已拒绝)。Promise对象创建后处于pending状态,并在异步操作完成后转换为fulfilled或rejected状态。
// 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)
}
});
// then()用来处理resolve的数据 data成功后返回的数据
p1
.then(data=>{
console.log(data);
})
.catch(err=>{
console.log(err);
})
.finally(()=>{
console.log('最终执行')
})
最基本的用法是通过构造函数实例化一个Promise对象,同时传入一个带有两个参数的函数,通常称为executor函数。executor函数接收两个参数:resolve和reject,分别表示异步操作成功和失败时的回调函数。
Promise对象创建后,可以使用then方法和catch方法指定fulfilled状态和rejected状态的回调函数。then方法可接受两个参数,一个处理fulfilled状态的函数,另一个处理rejected状态的函数。只传一个参数则表示当Promise对象状态变为fulfilled时,then方法会自动调用这个回调函数,并将Promise对象的结果作为参数传递给它。使用catch方法注册一个回调函数,用于处理“失败”的结果,即捕获Promise的状态改变为rejected状态或操作失败抛出的异常
(3)async/await
async/await是一种用于处理异步操作的Promise语法,使得编写异步代码变得更加简单和易读。通过使用async关键字声明一个函数为异步函数,并使用await关键字等待Promise的解析(完成或拒绝),以同步的方式编写异步操作的代码。
async函数是一个返回Promise对象的函数,用于表示一个异步操作。在async函数内部,可以使用await关键字等待一个Promise对象的解析,并返回其解析值。如果一个async函数抛出异常,那么该函数返回的Promise对象将被拒绝,并且异常信息会被传递给Promise对象的onRejected()方法
// async函数 达到的效果是同步代码的效果
//1.生成一个promise es6
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()
由于要等待异步操作完成,因此需要将整个操作包在async函数中。除了在async函数中使用await外,还可以使用try/catch块来捕获异步操作中的异常
更多推荐




所有评论(0)