鸿蒙-Promise 并发(JS)

Promise 并发(JS)

在 async 函数中,“过度 await”代码非常普遍。将 Promise.all() 与异步函数一起使用,可以有效的实现并发。

例如,两个异步函数 fetchA, fetchB:

1
2
3
4
5
6
7
8
9
async function fetchA() {
const response = await fetch("/path/a");
return await response.json();
}

async function fetchB() {
const response = await fetch("/path/b");
return await response.json();
}

await 运算符会让异步函数串行执行,执行 fetchA()获得结果之后,才去执行 fetchB()。如下:

1
2
3
4
5
async function awaitFunc() {
const a = await fetchA();
const b = await fetchB();
return handleResult(a, b);
}

我们可以使用 Promise.all 让异步函数并发执行。

1
2
3
4
async function promiseAllFunc() {
const [a, b] = await Promise.all([fetchA(), fetchB()]);
return handleResult(a, b);
}

Promise 类提供了以下四种异步任务的并发。

Ox01 Promise.all()

Promise.all() 静态方法接受一个 Promise 可迭代对象作为输入,并返回一个 Promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组。

  • 所有的 Promise 都被兑现时兑现,并返回一个包含所有兑现值的数组;
  • 在任何一个输入的 Promise 被拒绝时立即拒绝,并带回被拒绝的原因;
1
2
3
4
5
6
7
8
9
10
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "foo");
});

Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// Expected output: Array [3, 42, "foo"]

Ox02 Promise.allSettled()

Promise.allSettled() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个单独的 Promise。当所有输入的 Promise 都已敲定时(包括传入空的可迭代对象时),返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组。

  • 在所有的 Promise 都被敲定时兑现,返回一个带有描述每个 Promise 结果的对象数组;
  • 永远不会被 reject;
1
2
3
4
5
6
7
8
9
10
11
12
13
Promise.allSettled([
Promise.resolve(33),
new Promise((resolve) => setTimeout(() => resolve(66), 0)),
99,
Promise.reject(new Error("一个错误")),
]).then((values) => console.log(values));

// [
// { status: 'fulfilled', value: 33 },
// { status: 'fulfilled', value: 66 },
// { status: 'fulfilled', value: 99 },
// { status: 'rejected', reason: Error: 一个错误 }
// ]
  • status: 一个字符串,要么是 “fulfilled”,要么是 “rejected”,表示 promise 的最终状态。
  • value: 仅当 status 为 “fulfilled”,才存在。promise 兑现的值。
  • reason: 仅当 status 为 “rejected”,才存在,promsie 拒绝的原因。

Ox03 Promise.any()

在任意一个 Promise 被兑现时兑现;仅在所有的 Promise 都被拒绝时才会拒绝。

1
2
3
4
5
6
7
8
9
const promise1 = Promise.reject(0);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, "quick"));
const promise3 = new Promise((resolve) => setTimeout(resolve, 500, "slow"));

const promises = [promise1, promise2, promise3];

Promise.any(promises).then((value) => console.log(value));

// Expected output: "quick"

Ox04 Promise.race()

在任意一个 Promise 被敲定时敲定。换句话说,在任意一个 Promise 被兑现时兑现;在任意一个的 Promise 被拒绝时拒绝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
function sleep(time, value, state) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (state === "兑现") {
return resolve(value);
} else {
return reject(new Error(value));
}
}, time);
});
}

const p1 = sleep(500, "一", "兑现");
const p2 = sleep(100, "二", "兑现");

Promise.race([p1, p2]).then((value) => {
console.log(value); // “二”
// 两个都会兑现,但 p2 更快
});

const p3 = sleep(100, "三", "兑现");
const p4 = sleep(500, "四", "拒绝");

Promise.race([p3, p4]).then(
(value) => {
console.log(value); // “三”
// p3 更快,所以它兑现
},
(error) => {
// 不会被调用
}
);

const p5 = sleep(500, "五", "兑现");
const p6 = sleep(100, "六", "拒绝");

Promise.race([p5, p6]).then(
(value) => {
// 不会被调用
},
(error) => {
console.error(error.message); // “六”
// p6 更快,所以它拒绝
}
);

参考

MDN Web 开发技术:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

鸿蒙-MD5摘要(ArkTS)

SDK 版本:HarmonyOS NEXT Developer Beta2 SDK (5.0.0.31)
DevEco-Studio 版本:DevEco Studio NEXT Developer Beta2 (5.0.3.502)
工程机版本:ALN-AL00 NEXT.0.0.31

简介

MD5 算法常常被用来验证网络文件传输的完整性,防止文件被人篡改。MD5 全称是报文摘要算法(Message-Digest Algorithm 5),此算法对任意长度的信息逐位进行计算,产生一个二进制长度为 128 位(十六进制长度就是 32 位)的“指纹”(或称“报文摘要”),不同的文件产生相同的报文摘要的可能性是非常非常之小的。

Linux 下 md5sum 用法 (查看文件或字符串的 md5 值)

md5sum 命令采用 MD5 报文摘要算法(128 位)计算和检查文件的校验和。一般来说,安装了 Linux 后,就会有 md5sum 这个工具,直接在命令行终端直接运行。

windows 下如果安装了 git-bash 也可以在所在的文件夹下,右击,选择[Git Bash Here],也可以直接使用 md5sum 命令。

MD5 ArkTS 实现代码

MD5Util.ets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/*
* Copyright (c) 2024. Dench.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { cryptoFramework } from "@kit.CryptoArchitectureKit";
import { buffer } from "@kit.ArkTS";
import { hash } from "@kit.CoreFileKit";
import { systemDateTime } from "@kit.BasicServicesKit";

/**
* 计算字符串Md5
*/
export async function md5(messageText: string): Promise<string> {
const md = cryptoFramework.createMd("MD5");
await md.update({
data: new Uint8Array(buffer.from(messageText, "utf-8").buffer),
});
const mdOutput = await md.digest();
console.info("md mdOutput: " + mdOutput.data);
let mdLen = md.getMdLength();
console.info("md mdLen: " + mdLen);
const result = buffer.from(mdOutput.data.buffer).toString("hex");
console.info("md result: " + result);
return result;
}

// 文件Md5, 哈希计算采用的算法。可选 "md5"、"sha1" 或 "sha256"。建议采用安全强度更高的 "sha256", 此处使用"md5"
export async function fileMd5(filePath: string): Promise<string> {
try {
const t = systemDateTime.getTime();
const result = await hash.hash(filePath, "md5");
console.info("fileMd5: result=" + result);
console.info(`fileMd5: duration=${systemDateTime.getTime() - t}`);
return result;
} catch (err) {
console.error(`fileMd5: ${JSON.stringify(err)}`);
return "";
}
}

// 文件MD5,大文件读写效率低,有性能瓶颈,采用上面的文件Hash算法
// export async function fileMd5(path: string, algName: string = "MD5"): Promise<string> {
// console.info("fileMd5: path=" + path)
// let md = cryptoFramework.createMd(algName);
// try {
// const t = systemDateTime.getTime();
// let file = await fs.open(path, fs.OpenMode.READ_ONLY);
// let arrayBuffer = new ArrayBuffer(4096);
// let offset = 0;
// let readOptions: ReadOptions = {
// offset: offset,
// length: 4096
// };
// let len = await fs.read(file.fd, arrayBuffer, readOptions);
// while (len > 0) {
// // console.info("md read file len: " + len);
// const bf = buffer.from(arrayBuffer).subarray(0, len);
// // console.info("md bf: " + bf.buffer.byteLength);
// const uint8 = new Uint8Array(bf.buffer);
// // console.info("md uint8: " + uint8.byteLength);
// await md.update({ data: uint8 });
// offset += len;
// readOptions.offset = offset;
// len = await fs.read(file.fd, arrayBuffer, readOptions);
// }
// try {
// fs.close(file);
// } catch (e) {
// console.info(JSON.stringify(e));
// }
// let mdOutput = await md.digest();
// // console.info("md mdOutput: " + mdOutput.data);
// let mdLen = md.getMdLength();
// // console.info("md mdLen: " + mdLen);
// const result = buffer.from(mdOutput.data.buffer).toString('hex');
// console.info("md succeed: " + result);
// console.info(`md duration=${systemDateTime.getTime() - t}`);
// return result;
// } catch (e) {
// console.error("md error: " + JSON.stringify(e));
// return ''
// }
// }

Reference

https://www.cnblogs.com/kevingrace/p/10201723.html
https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-file-hash-V5

鸿蒙-Text组件使用自定义字体

DevEco Studio 版本:DevEco Studio NEXT Developer Preview2
HarmonyOS API 版本:4.1.0(11)

1 导入字体文件

将字体文件BebasNeue-Regular.ttf放在项目的resources/rawfile文件夹下,如下图:

20240604201941

2 注册自定义字体

需要在组件的 aboutToAppear() 方法中,使用font注册自定义字体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import font from '@ohos.font';

@Component
export struct CustomFontComponent {
aboutToAppear(): void {
font.registerFont({
familyName: 'BebasNeue',
familySrc: $rawfile('BebasNeue-Regular.ttf')
})
}

build() {
Column() {
Text('9999')
.fontSize(17)
.fontColor('#333333')
.fontFamily('BebasNeue')
.maxLines(1)
.textAlign(TextAlign.Center)
.fontWeight(FontWeight.Regular)
.textOverflow({
overflow: TextOverflow.Ellipsis
})
.height('100%')
.width('100%');
}
}
}

3 使用已注册的字体

在Text组件中使用已注册的字体,设置fontFamily为已注册的familyName即可。

参考

https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-font-V5

鸿蒙-下拉刷新控件PullToRefresh使用

DevEco Studio 版本:DevEco Studio NEXT Developer Preview2
HarmonyOS API 版本:4.1.0(11)
PullToRefresh 版本:"@ohos/pulltorefresh": "2.0.5"

下拉刷新控件 PullToRefresh 使用

  • 支持自定义 header,footer
  • 没有更多了布局

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { HomeVM } from '../vm/HomeVM';
import { PullToRefresh } from '@ohos/pulltorefresh';

@Component
export struct ListAreaComponent {
@State data?: ListDataWrapper[] | null = null
private vm: HomeVM = new HomeVM()
// 需绑定列表或宫格组件
private scroller: Scroller = new Scroller();

aboutToAppear(): void {
// request data.
this.vm.requestData().then((data: ListDataWrapper[]) => {
this.data = data;
})
}

build() {
PullToRefresh({
// 必传项,列表组件所绑定的数据
data: this.data,
// 必传项,需绑定传入主体布局内的列表或宫格组件
scroller: this.scroller,
// 必传项,自定义主体布局,内部有列表或宫格组件
customList: () => {
// 一个用@Builder修饰过的UI方法
this.getListView()
},
// 可选项,下拉刷新回调
onRefresh: () => {
return new Promise<string>((resolve) => {
this.vm.requestData().then((data: ListDataWrapper[]) => {
this.data = data
resolve('')
}).catch((error: Error) => {
resolve('')
});
});
},
// 可选项,上拉加载更多回调
onLoadMore: () => {
return new Promise<string>((resolve) => {
resolve('')
});
},
customLoad: commonNoMore,
customRefresh: commonLoading,
})
.width('100%')
.height('100%')
}

@Builder
getListView() {
List({ space: 12, scroller: this.scroller }) {
ForEach(this.data, (item: ListDataWrapper, index: number) => {
ListItem() {
// ...
};
}, (item: ListDataWrapper, index?: number) => index + JSON.stringify(item));
}
.width('100%')
.height('100%')
.scrollBar(BarState.Off)
.edgeEffect(EdgeEffect.None)
}
}


@Builder
export function commonLoading(): void {
Stack() {
// Text(refreshText)
// .textAlign(TextAlign.Center)
// .fontColor( 0)
// .fontSize( 0)
Stack() {
Canvas(new CanvasRenderingContext2D(new RenderingContextSettings(true)))
.width('100%')
.height('100%')
.onReady(() => {
// this.initCanvas();
}) // .visibility(this.state == IS_PULL_DOWN_2 ? Visibility.Visible : Visibility.Hidden)
// .visibility(Visibility.Hidden)
LoadingProgress()
.color('#FF00A3FF')
.width(32)
.height(32)
}
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
.clip(true)
}

@Builder
export function commonNoMore(): void {
Stack() {
Text('已经到底了~')
.textAlign(TextAlign.Center)
.fontColor('#FF85888F')
.fontSize(14)
}
.width('100%')
.height('100%')
.clip(true)
}

@Builder
export function commonEmpty(): void {
Stack()
.width('100%')
.height('100%')
}

注意,列表组件需要设置为无边缘效果:List().edgeEffect(EdgeEffect.None)

参考

https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Fpulltorefresh

为git仓库设置独立的账户和邮箱

为 git 仓库设置独立的账户和邮箱

0x01 配置 Git 全局默认的账户和邮箱

1
2
3
4
5
6
## 全局默认的用户名和邮箱
$ git config --global user.name "aaa"
$ git config --global user.email "aaa@gmail.com"

## 避免每次git操作都需要输入用户名密码
$ git config --global credential.helper store

0x02 为 git 仓库设置独立的账户和邮箱

使用编辑器打开.git/config文件,直接在文件的最后添加以下信息:

1
2
3
[user]
name = bbb
email = bbb@gmail.com

解析<em></em>标签高亮显示

DevEco Studio 版本:DevEco Studio NEXT Developer Preview2
HarmonyOS API 版本:4.1.0(11)

解析标签高亮显示

由于跟后端约定,接口中对于返回的字符串中,使用标签的内容需要使用主题色高亮显示。比如 <em>权力</em>的<em>游戏</em> 第<em>七</em>季

注意:当前版本不支持标签嵌套。

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Text() {
buildHighLightSpan(item.title)
}
.fontSize(17)
.fontColor('#FF222222')
.fontWeight(600)
.textOverflow({
overflow: TextOverflow.Ellipsis
})
.width('100%')


/**
* 解析 <em> </em>标签高亮显示
*
* 注意:不支持嵌套
*
* @param highLightTitle 带标签的文本
*/
@Builder
function buildHighLightSpan(highLightTitle: string | undefined) {
if (highLightTitle == null || highLightTitle.indexOf('<em>') == -1 || highLightTitle.indexOf('</em>') == -1) {
Span(highLightTitle?.replace('<em>', '').replace('</em>', '')).fontColor('#FF222222')
} else {
ForEach(highLightTitle.split('</em>'), (attr: string) => {
ForEach(attr.split('<em>'), (item: string, index: number) => {
if (item != null || item != '') {
if (index == 0) {
Span(item).fontColor('#FF222222');
} else {
Span(item).fontColor('#FF00A3FF');
}
}
});
})
}
}

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×