ArkTS語(yǔ)言(六)
學(xué)習ArkTS語(yǔ)言
ArkTS是HarmonyOS的主要應用開(kāi)發(fā)語(yǔ)言,在TypeScript基礎上進(jìn)行了擴展,保留了其基本風(fēng)格,并通過(guò)增強靜態(tài)檢查和分析來(lái)提高程序的穩定性和性能。本教程將幫助開(kāi)發(fā)者掌握ArkTS的核心功能、語(yǔ)法及最佳實(shí)踐,以便高效地構建高性能移動(dòng)應用。
1.異步任務(wù)
1.1 異步任務(wù)概述
在學(xué)習異步任務(wù)之前,我們先對“任務(wù)”這個(gè)詞下一個(gè)定義——任務(wù)是完成某項操作要執行的一段代碼。
一個(gè)任務(wù)可以執行任何特定操作的代碼,可能是簡(jiǎn)單的打印語(yǔ)句、復雜的算法處理、網(wǎng)絡(luò )請求或 I/O 操作等。這些任務(wù)的執行時(shí)間各不相同:打印語(yǔ)句執行時(shí)間通常很短,而網(wǎng)絡(luò )請求和 I/O 操作可能耗時(shí)較長(cháng)。
由于 JavaScript 執行引擎是單線(xiàn)程的,如果按照順序執行所有任務(wù),耗時(shí)較長(cháng)的任務(wù)可能會(huì )阻塞線(xiàn)程,導致整體性能下降,進(jìn)而影響用戶(hù)體驗。

為了更高效地管理和執行這些任務(wù),JavaScript 將要執行的任務(wù)分為同步任務(wù)和異步任務(wù),使得JavaScript 能夠在單線(xiàn)程環(huán)境中有效地處理耗時(shí)操作,而不阻塞主線(xiàn)程。
● 同步任務(wù):這些是立即執行的任務(wù),不需要等待任何條件或事件,會(huì )阻塞后續代碼的執行。
● 異步任務(wù):這些任務(wù)不會(huì )立即執行,而是先掛起,被推遲到未來(lái)某個(gè)時(shí)間點(diǎn)執行,不會(huì )阻塞后續代碼的執行。

示例代碼
在 ES6(ECMAScript 2015,JavaScript標準規范) 之前,JavaScript 的異步任務(wù)主要是通過(guò)如 setTimeout, setInterval, I/O 操作 等)來(lái)實(shí)現。
//同步代碼
console.log("Start"); //異步任務(wù)
setTimeout(() => {console.log("異步任務(wù)1");
}, 0);//異步任務(wù)
setTimeout(() => {console.log("異步任務(wù)2");
}, 0);setInterval(()=>{console.log("異步任務(wù)3");
}, 1000)//同步代碼
console.log("End");
完整輸出順序:
Start
End
異步任務(wù)1
異步任務(wù)2
異步任務(wù)3 (1 秒后開(kāi)始循環(huán)輸出)
異步任務(wù)3 (再次循環(huán)輸出)
異步任務(wù)3 (繼續每秒輸出)
...
解釋?zhuān)?/strong>
- console.log("Start"); 和 console.log("End"); 是同步代碼,立即執行。
- setTimeout 的回調雖然是 0 毫秒延遲,但它們是異步任務(wù),會(huì )等到當前同步任務(wù)執行完后再執行。
- setInterval 會(huì )每隔 1 秒重復執行其回調,每次執行都是一個(gè)新的宏任務(wù)。
1.2 事件循環(huán)機制
異步任務(wù)之所以能夠被推遲到未來(lái)的某個(gè)時(shí)間點(diǎn)執行,而不阻塞主線(xiàn)程,主要依賴(lài)于事件循環(huán)機制和任務(wù)隊列來(lái)實(shí)現的。
● 事件循環(huán)機制:是一種調度異步任務(wù)的模型。
● 任務(wù)隊列:任務(wù)隊列是用來(lái)存儲異步任務(wù)的容器,等待后續某個(gè)時(shí)間點(diǎn),被事件循環(huán)調度。
下圖展示了JavaScript執行引擎啟動(dòng)后,各個(gè)任務(wù)的執行流程。

在 ES6 之前(ECMAScript 2015,JavaScript標準規范),所有異步任務(wù)都被放在同一個(gè)任務(wù)隊列中,缺乏更精細的控制,所以從ES6開(kāi)始引入了宏任務(wù)和微任務(wù)對異步任務(wù)進(jìn)行更精細的控制。
1.3 宏任務(wù)和微任務(wù)
從ES6開(kāi)始引入了大量的新特性(包括 Promise 和 微任務(wù))。引入微任務(wù)是為了優(yōu)化任務(wù)調度,減少延遲,提高性能,可以更高效地處理一些高優(yōu)先級的小任務(wù)。從此異步任務(wù)被細分為宏任務(wù)和微任務(wù)。
● 微任務(wù):具有高優(yōu)先級的異步任務(wù),通常用于較短時(shí)間內的異步操作,如 Promise 等操作。
● 宏任務(wù):具有較低優(yōu)先級的異步任務(wù),通常用于延遲執行或需要等待的操作,如 setTimeout 等操作。
這種任務(wù)劃分和管理機制使得 JavaScript 能夠在單線(xiàn)程環(huán)境中有效地處理異步任務(wù),提升程序的響應性和用戶(hù)體驗。
JavaScript 執行引擎通過(guò)事件循環(huán)機制、宏任務(wù)隊列、微任務(wù)隊列在管理并執行異步任務(wù)。如下圖所示

解釋?zhuān)?/strong>
● JavaScript 執行引擎啟動(dòng)后,開(kāi)啟一條主線(xiàn)程,按照代碼順序依次往下執行;
● 當執行到宏任務(wù)時(shí),將宏任務(wù)添加到宏任務(wù)隊列,等待后續事件循環(huán)調度,JavaScript主線(xiàn)程繼續往下執行;
● 當執行到微任務(wù)時(shí),將微任務(wù)添加到微任務(wù)隊列,等待后續事件循環(huán)調度,JavaScript主線(xiàn)程繼續往下執行,直到所有同步代碼執行完畢;
● 執行完所有同步代碼之后,啟動(dòng)事件循環(huán),先依次執行完微任務(wù)隊列中所有的微任務(wù),直到微任務(wù)微空。
● 再執行一個(gè)宏任務(wù)后,判斷微任務(wù)隊列是否為空(因為可能有新的微任務(wù)加入到微任務(wù)隊列)
● 如果微任務(wù)隊列不為空,則繼續執行微任務(wù)隊列中的所有微任務(wù),如此循環(huán)。
//同步代碼
console.log("Start"); //宏任務(wù): 該回調函數的任務(wù)會(huì )被添加到宏任務(wù)隊列
setTimeout(() => {console.log("宏任務(wù)1");
}, 0);setTimeout(() => {console.log("宏任務(wù)2");
}, 0);//微任務(wù)
Promise.resolve().then(() => {console.log("微任務(wù)1");
});//微任務(wù)
Promise.resolve().then(() => {console.log("微任務(wù)2");
});//同步代碼
console.log("End");
完整輸出順序:
Start
End
微任務(wù)1
微任務(wù)2
宏任務(wù)1
宏任務(wù)2
解釋?zhuān)?/strong>
- 先執行同步代碼,輸出: Start 和 End。
- setTimeout() 創(chuàng )建的宏任務(wù)回調被加入 宏任務(wù)隊列。
- Promise.resolve().then() 創(chuàng )建的微任務(wù)回調被加入 微任務(wù)隊列。
- 同步代碼執行完后,開(kāi)啟事件循環(huán),會(huì )執行完微任務(wù)隊列中所有的微任務(wù),輸出: 微任務(wù)1、 微任務(wù)2
- 執行完微任務(wù)后,事件循環(huán)會(huì )從宏任務(wù)隊列中取出任務(wù)執行,輸出: 宏任務(wù)1、宏任務(wù)2
1.4 處理異步任務(wù)的結果
前面我們學(xué)習了什么是異步任務(wù)以及其執行機制,了解到異步任務(wù)的一個(gè)重要特點(diǎn)是:“異步任務(wù)不會(huì )立即執行,并且不會(huì )阻塞主線(xiàn)程,它將在所有同步代碼執行完畢后再開(kāi)始執行?!比欢?,這種特性也引發(fā)了一個(gè)新的問(wèn)題。
問(wèn)題:
異步任務(wù)通常涉及耗時(shí)操作,如網(wǎng)絡(luò )請求、文件讀取等,這些操作的完成時(shí)間取決于具體的需求和運行環(huán)境的狀態(tài)(如網(wǎng)絡(luò )狀況、設備性能等)。在實(shí)際開(kāi)發(fā)中,我們經(jīng)常需要根據異步操作的執行結果(成功或失?。﹣?lái)決定接下來(lái)的操作。
然而,由于異步操作在未完成之前無(wú)法獲知其狀態(tài),也無(wú)法預測它具體會(huì )花費多長(cháng)時(shí)間。因此,在異步任務(wù)執行過(guò)程中,我們無(wú)法直接處理后續邏輯。為了避免在等待過(guò)程中阻塞其他任務(wù),我們需要一種機制,能夠在異步任務(wù)執行完成后,根據其結果(成功或失?。﹣?lái)決定后續操作。無(wú)論異步任務(wù)執行成功還是失敗,我們必須在編寫(xiě)代碼時(shí)提前定義好這些處理邏輯,這樣程序在執行時(shí),才能根據任務(wù)的結果自動(dòng)調用預設的處理方式。
Promise 是一種用于處理異步操作的 JavaScript 對象。它代表一個(gè)未來(lái)可能完成(或失?。┑牟僮骷捌浣Y果值。
Promise 是一種用于處理微任務(wù)的工具。同時(shí)Promise也是一個(gè)對象,它有三種狀態(tài):pending(待定)、fulfilled(已完成)和 rejected(已拒絕)。這些狀態(tài)決定了 Promise 對象如何處理異步任務(wù)的結果。
Promise的三種狀態(tài):
- pending(待定):
○ 這是 Promise 對象的初始狀態(tài),表示異步操作還沒(méi)有完成。
○ 這個(gè)狀態(tài)下,Promise 既沒(méi)有成功也沒(méi)有失敗。- fulfilled(已完成):
○ 當異步操作成功完成時(shí),Promise 會(huì )從 pending 狀態(tài)變?yōu)?fulfilled 狀態(tài)。
○ 成功時(shí)調用 resolve(),并且 resolve() 的參數會(huì )作為后續 .then() 方法的回調函數的參數。- rejected(已拒絕):
○ 當異步操作失敗時(shí),Promise 會(huì )從 pending 狀態(tài)變?yōu)?rejected 狀態(tài)。
○ 失敗時(shí)調用 reject(),并且 reject() 的參數會(huì )作為后續 .catch() 方法的回調函數的參數。
Promise三種狀態(tài)的轉化流程如下圖所示:

Promise 對象一旦從 pending 狀態(tài)轉換為 fulfilled 或 rejected,就會(huì )被鎖定在這個(gè)狀態(tài),無(wú)法再改變狀態(tài)。也就是說(shuō),Promise 一旦被 resolve 或 reject,它的狀態(tài)就不會(huì )再發(fā)生變化。
1.5 如何創(chuàng )建Promise對象
從語(yǔ)法角度來(lái)說(shuō),Promise 是一個(gè)對象,它封裝了異步操作的最終結果,并提供了兩種回調函數來(lái)處理成功和失敗的情況。這兩個(gè)回調函數分別是:
● resolve:表示異步操作成功時(shí)調用的回調函數。
● reject:表示異步操作失敗時(shí)調用的回調函數。
const promise = new Promise((resolve, reject) => {// 模擬異步操作let success = true;if (success) {resolve("操作成功!");} else {reject("操作失??!");}
});
在上述代碼中,promise 對象的狀態(tài)會(huì )根據 success 的值變?yōu)?fulfilled 或 rejected。
1.6 使用 .then() 和 .catch() 處理結果
一旦 Promise 的狀態(tài)改變,你可以使用 .then() 來(lái)處理成功的結果,使用 .catch() 來(lái)處理失敗的原因。
● then():用來(lái)指定當 Promise 成功完成時(shí)需要執行的回調函數。
● catch():用來(lái)指定當 Promise 失敗時(shí)需要執行的回調函數。
promise.then(result => {console.log(result); // "操作成功!"}).catch(error => {console.log(error); // "操作失??!"});
1.7 鏈式調用
當你調用 then() 或者catch() 方法時(shí),你可以傳入一個(gè)回調函數,這個(gè)回調函數會(huì )在 Promise 狀態(tài)變?yōu)?fulfilled或者reject 時(shí)執行。這個(gè)回調函數可以返回一個(gè)普通值,也可以返回一個(gè)新的 Promise。
● 如果回調函數返回一個(gè)普通值,該值會(huì )被包裝成一個(gè) resolved 狀態(tài)的 Promise,并傳遞到下一個(gè) then() 方法中。
● 如果回調函數返回一個(gè) Promise 對象,那么下一個(gè) then() 將會(huì )等待這個(gè)新的 Promise 完成后再繼續執行。
這使得你可以鏈式處理異步操作,并且 then() 方法始終返回一個(gè)新的 Promise,從而可以繼續使用 .then() 進(jìn)行鏈式調用。
示例 1:回調函數返回一個(gè)普通值
new Promise((resolve, reject) => {resolve("Hello");
}).then(result => {console.log(result); // 輸出 "Hello"return "World"; // 返回一個(gè)普通值,自動(dòng)風(fēng)作為resolved("World")狀態(tài)的Promise對象}).then(result => {console.log(result); // 輸出 "World"});
在這個(gè)例子中,then() 回調函數返回了一個(gè)普通值 "World"。這個(gè)值被自動(dòng)包裝為一個(gè)已解決的 Promise,然后傳遞給下一個(gè) .then() 回調函數。
示例 2:回調函數返回一個(gè) Promise
new Promise((resolve, reject) => {resolve("Hello");
}).then(result => {console.log(result); // 輸出 "Hello"return new Promise((resolve, reject) => {setTimeout(() => {resolve("World");}, 1000);}); // 返回一個(gè)新的 Promise}).then(result => {console.log(result); // 輸出 "World"(等待 1 秒)});
在這個(gè)例子中,then() 回調函數返回了一個(gè)新的 Promise 對象。下一個(gè) then() 會(huì )等待這個(gè) Promise 被解析(resolve("World"))之后,再繼續執行回調。
1.8 異常捕獲
你可以在鏈式調用中使用 .catch() 來(lái)捕獲和處理任何一步操作中的錯誤。即使在鏈中的某個(gè) .then() 發(fā)生了錯誤,錯誤會(huì )被傳遞到 .catch() 中。
觸發(fā) catch 的方式有下面幾種情況:
● 當Promise處于pending狀態(tài)時(shí)
○ 手動(dòng)調用 reject(),將 Promise 設為 rejected 狀態(tài), 觸發(fā)catch。
○ 使用throw主動(dòng)拋出異常,此時(shí)也會(huì )觸發(fā)catch
● 當Promise處于fulfilled狀態(tài)時(shí)
○ 通過(guò)throw拋出異常,會(huì )讓當前的異步操作失敗,從而觸發(fā) catch。
○ 通過(guò)返回一個(gè)被拒絕的 Promise,它會(huì )導致 Promise 進(jìn)入 rejected 狀態(tài),從而觸發(fā) catch。
示例1:當Promise處于pending狀態(tài)時(shí),throw拋出異常,觸發(fā)catch
new Promise((resolve, reject) => {throw new Error("Something went wrong");
}).catch(error => {console.log(error.message); // 輸出 "Something went wrong"});
示例2:當Promise處于fulfilled狀態(tài)時(shí),throw拋出異常,觸發(fā)catch
new Promise((resolve, reject) => {resolve("Success")
}).then(result=>{console.log(result); //Successthrow new Error("出錯了。。。")}).catch(error => {console.log(error); //出錯了。。。});
示例3:當Promise處于fulfilled狀態(tài)時(shí),返回一個(gè)被拒絕的Promise,觸發(fā)catch
new Promise((resolve, reject) => {resolve("Success")
}).then(result=>{console.log(result); //Successreturn new Promise((resolve,reject)=>{reject("Failed。。。")})}).catch(error => {console.log(error); //Failed。。。});
Promise.resolve(value) 返回一個(gè)已解決(fulfilled)的 Promise,并將 value 作為結果返回。
Promise.reject(reason) 返回一個(gè)已拒絕(rejected)的 Promise,并將 reason 作為失敗的原因返回。
1.9 Promise.resolve()
Promise.resolve() 是一個(gè)用于快速創(chuàng )建已解決的 Promise 對象的工具方法,能夠處理普通值、Promise 對象。
// value:可以是任意值,也可以是一個(gè) Promise 對象。
Promise.resolve(value);
如果value傳入的值已經(jīng)是一個(gè) Promise,則直接返回該 Promise;如果傳入的值是一個(gè)普通值(如字符串、數字等),則返回一個(gè)已解決的 Promise,并將該值作為結果傳遞給 then() 的回調函數。
示例1:傳入普通值
//Promise.resolve(42) 返回一個(gè)已經(jīng)解決的 Promise,并將 42 作為結果傳遞給 then() 回調。
const promise = Promise.resolve(42);
promise.then(result => {console.log(result); // 輸出 42
});
示例2:傳入另一個(gè)已解決的 Promise
const existingPromise = Promise.resolve("Hello, World!");
// 直接返回傳入的 existingPromise,因為它已經(jīng)是一個(gè) Promise,所以不會(huì )創(chuàng )建新的 Promise。
const promise = Promise.resolve(existingPromise);promise.then(result => {console.log(result); // 輸出 "Hello, World!"
});
1.10 Promise.rejected()
與 Promise.resolve() 類(lèi)似,Promise.reject() 用于快速創(chuàng )建一個(gè)被拒絕的 Promise,并且可以傳遞拒絕的原因。
//reason:拒絕的原因,可以是任意類(lèi)型的值(通常是一個(gè)錯誤對象或錯誤消息),它將作為 catch() 方法的回調函數的參數。
Promise.reject(reason);
Promise.reject() 返回一個(gè) Promise 對象,它的狀態(tài)被立即設置為 rejected,并將傳入的拒絕原因作為參數傳遞給 catch() 回調函數。
示例1: 返回一個(gè)已經(jīng)拒絕的Promise
const promise = Promise.reject("Something went wrong");
promise.catch(error => {console.log(error); // 輸出 "Something went wrong"
});
這里,Promise.reject("Something went wrong") 返回一個(gè)已拒絕的 Promise,并將 "Something went wrong" 作為錯誤原因傳遞給 catch() 方法。
示例2:返回一個(gè)已拒絕的 Promise,拒絕原因為 Error 對象
const promise = Promise.reject(new Error("Network Error"));
promise.catch(error => {console.log(error.message); // 輸出 "Network Error"
});
這里,Promise.reject(new Error("Network Error")) 返回一個(gè)已拒絕的 Promise,并將 Error 對象傳遞給 catch() 方法。catch() 中可以通過(guò) error.message 獲取錯誤的具體信息。
示例3:鏈式調用中的錯誤傳播
在 Promise 鏈中的任何一個(gè) Promise 被拒絕時(shí),后續的 catch() 會(huì )捕獲到該錯誤,即使該拒絕發(fā)生在鏈中的任意一環(huán)。
Promise.resolve("Start").then(result => {console.log(result);return Promise.reject("Error occurred");}).catch(error => {console.log(error); // 輸出 "Error occurred"});
2. 模塊
通常一個(gè)文件中的變量、函數、類(lèi)、接口等只能在當前類(lèi)中使用。但是你可以使用 export 關(guān)鍵字來(lái)標記一個(gè)變量、函數、類(lèi)或類(lèi)型,使其能夠在其他文件中被導入和使用。
2.1 導出
例:在A(yíng).ets文件中有一個(gè)Student類(lèi),還有print函數,都是export導出
//導出Student類(lèi)
export class Student{name: stringage: numberconstructor(name: string='', age: number=0) {this.name = namethis.age = age}show():void{console.log(`學(xué)生的姓名是${this.name},學(xué)生的年齡是${this.age}`)}
}//導出print函數
export function print(){console.log(`A 文件中的 a函數執行了`)
}
2.2 導入
例:然后在B.ets文件中,導入Student類(lèi)、print函數,就可以調用它們了。
//1.先導入A文件中的Student類(lèi),和a函數
import { print, Student } from './A'//2.在使用
let s1 = new Student('張三',20)
print()
鴻蒙學(xué)習地址
