阿爾法酷推出 3 款企業級散熱風扇:支援卡住保護,最高 1.5 萬 RPM
技術標籤:typescriptnodejs
Typescript學習筆記
什麼是Typescript
TypeScript是一種由微軟開發的開源、跨平臺的程式語言。它是JavaScript的超集,最終會被編譯為JavaScript程式碼;TypeScript適合用來編寫基於node的大型應用;ts的靜態語言特性,能幫助我們在開發時候就發現自己語法的錯誤,而非像其他js在執行時才發現;ts的型別定義,讓程式碼更加規範,在呼叫一些方法和api時,能準確的得到方法引數和返回值提示,減少了以往js需要api文件才能開發的問題;
Typescript 的資料型別
基本資料型別
布林型別(boolean); 數字型別(number); 字串型別(string); 陣列型別(array); 元組型別(tuple); 列舉型別(enum); 任意值型別(any); null和undefined; void型別; never型別;
常用型別(與java的定義差不多)
//最常用的型別
let bool:boolean = true;
let str : String = "字串";
let num : number = 1.2;
//兩種陣列的定義方式
let strArr : string[] = ["1","23","123"];
let numArr : Array<Number> = [1,2,3];
Typescript 的強型別判斷非常嚴格,當試圖為上文的 str賦值 數字型別時,會報錯,體現了ts的靜態語言特性,強型別判斷
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-19lgYo9D-1612158764811)(.\img\2020-12-31_113203.png)]
//元組(Tuple)可以理解為一個長度,每一項元素型別都確定的陣列
let peopleArr: [string, string, number] = ['Dylan', 'male', 23] //陣列中元素的型別和長度都是固定的,與定義的型別和長度一樣
//混合型別的陣列
let arr:(number|string)[] = [12,'aa','a'];//定義陣列型別時,使用 | 連線(union type聯合型別),這資料可放入多種型別
any型別及其與object的區別
any任意值型別,在強調型別的ts中,ts物件可以讓被賦予如何型別,也可以從any中去任意屬性和方法(即使不存在的屬性和方法)
let testObj : any = {};
testObj=1;
testObj="aaa";
testObj={
sayHi(){
console.log("sayHi")
}
}
console.log(`testObj不存在的屬性---${testObj.aa}`)//呼叫不存在的屬性aa,編譯不報錯,執行也不儲存
console.log(`呼叫不存在的方法開始`)
testObj.sayNo();//不存在的方法
console.log(`呼叫不存在的方法結束`)
testObj.sayHi();
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-ubME4UVW-1612158764814)(./img\2020-12-31_143947.png)]
如上程式碼和執行結果可以看到,any修飾的變數,可以接受任意型別,可以呼叫不存在的屬性,但是不能呼叫不存在的方法;如上,呼叫不存在的方法,編譯不報錯,但是執行到 “呼叫不存在的方法開始`”這個log後,程式開始迴圈無法正常的往下走;any 類似原生的js物件,ts放棄對此類修飾物件的型別判斷;
any與object的區別,雖然在接受引數上兩者都能接收不同型別,但是編譯器,object 使用不存在的屬性和方法時,object型別編譯報錯
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-5hT6sUYs-1612158764814)(F:\學習筆記\img\2020-12-31_145203.png)]
any可以以賦值給任意型別(never型別除外),object則不能賦值給任意型別;any物件可以賦值給string,但是object不行
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-KzZJ7YZr-1612158764816)(F:\學習筆記\img\2020-12-31_145648.png)]
never any unkown
將三種類型一起講,是因為 any和unkown頂部型別,never是底端集合
any和unkown 是一切型別的超級型別(supertype)任何值都能冠以型別any和unkown
never是一個空集合,任何值都不能冠以型別 never;將某個值的型別解析為 never,編輯就會報錯;空集合可包含於任何非空集合,因此never 是一切其它非空型別的子集合。這就是為什麼 never 被稱為底端集合。
包裝型別
interface
以IEMP-SXTL-BFF專案程式碼來演示interface功能
//在src\inits\modelData\index.ts 向外暴露的CommonResultData
/**
* 服務返回資料通用結構
*/
export interface CommonResultData {
code: number;
msg: string;
data: Record<string, any>[] | null;
}
let data :CommonResultData = {
code:1,
msg:"aaa"
}
//這種定義,要求該型別的例項的物件,必須要有 code msg data屬性,以code為列,code可以 = 數字,null,underfined,但是不能沒有code屬性
//如果偏偏要讓 code 可有可無,則可以 code?: number; 使用問號,則屬性中沒有code,編譯也不會報錯
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-UNusM0NH-1612158764817)(C:\Users\tttare\AppData\Roaming\Typora\typora-user-images\image-20210104102645724.png)]
interface無法用new 例項化,class可以
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-IrDWkKha-1612158764818)(./img\2020-12-31_153131.png)]
如上圖,我們在使用interface修飾的型別時,能很清晰的看到這個型別所需要的屬性,這也是ts相比傳統js的優勢,能讓我們在沒有api文件的情況下,知道使用的型別中的屬性,屬性型別,方法,方法引數等資訊,這也是ts解決的痛點之一;
let comdata : CommonResultData = {
code:1001,
msg:"aass",
data:null
};//給CommonResultData型別賦值時,CommonResultData型別的三個屬性,必須全都要定義,不能是underfine,型別也不能不同
interface和java的介面還是很像的,interface能繼承interface,interface不能有方法體,不能用private修飾方法
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-I7NWzL78-1612158764818)(./img/2021-01-04_104047.png)]
ts建議給interface的方法確定返回值
class
在IEMP-SXTL-BFF專案中,class修飾的型別承擔更大的作用;class中有大量的方法和註解,主要用來編寫業務邏輯
//定義一個oprate類
class Oprate{
public sayHi(){
console.log('public say hi')
}
private sayHello(){
console.log('private say hello')
}
public static sayHalo(){
console.log('static say halo')
}
}
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-Hp1F1g6d-1612158764819)(./img/2021-01-04_101927.png)]
由上圖可知,class的例項能使用被public修改的方法,不能使用被private或static修飾的方法;static修改的方法,直接用型別呼叫
private 和 public都與java的類似,只是static修飾的方法,類的例項不能使用,與java不相同
extends ,implements,多型
interface不能被class繼承
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-nzb9WVco-1612158764819)(./img/2021-01-04_103116.png)]
interface能繼承interface,interface不能有private修飾方法,不能有方法體;
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-Zjyjwn8P-1612158764819)(./img/2021-01-04_103723.png)]
class實現interface必須也定義interface屬性,實現interface的方法
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-9OIxZT5f-1612158764820)(./img/2021-01-04_105612.png)]
class繼承class,能使用父class的方法及屬性
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-cswsJxdp-1612158764820)(./img/2021-01-04_110316.png)]
ts也有多型,interface接收實現類的例項,class接收子類的例項
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-iTP8lTq1-1612158764821)(./img/2021-01-04_110033.png)]
union(並集) 和 intersection(交集)
定義幾個class物件 用來測試交集和並集
interface OpInterface {
data:number;
sayToday():number;
}
class Oprate implements OpInterface{
data:number;
public sayToday():number{
return 12;
}
}
class OprateSon extends Oprate{
public saySon(){
console.log("son class")
}
}
class Dev {
code:number;
data:number;
public saySon(){
console.log("son class")
}
public sayDev(){
console.log("dev class")
}
}
class Mix {
code:number;
//下面的方法和屬性在 生成的並集中也不存在,但是new Mix可以賦值給並集type
t1 : string='str';
sayNo(){
}
}
class And {
data : number;
public saySon(){
console.log("son class")
}
}
並集,ts以 | 符號表示
//基本資料型別並集
type mixBasic = number|String;
let a2 : mixBasic = 1;
let a3 : mixBasic = 'str';
//包裝型別並集
type mix = Dev | OprateSon;
let devObj : mix = new Dev();
let opObj : mix = new OprateSon();
交集,ts以 & 符號表示
//基本資料型別
//mixBasic = number|String;她與string的交集 便是string
type andBasic = mixBasic&string;
let and1 : andBasic = "aaa";
//包裝型別並集
type mixOne = Dev | OprateSon;
type mixTwo = Dev | Oprate;
type and = mixOne & mixTwo;
let obj1 : and = new Dev();
let obj2 : and = new OprateSon();//mixOne 和 mixTwo 的交集應該只有 Dev型別,為啥可以賦值 OprateSon
//因為 OprateSon 繼承了 Oprate,所以ts繼承的特性也體現出來了 OprateSon&Oprate 產生的交集是 OprateSon
列舉
列舉定義方式
enum OprateEnum{
START,END
}
console.log("start..."+OprateEnum.START)
console.log("end..."+OprateEnum.END)
執行結果
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-3RM9Yfmo-1612158764821)(./img/2021-01-04_133820.png)]
可以看出,ts的列舉和java列舉有很大不同,在我們沒有給START,END賦值的情況下,列舉型別預設從0 開始自增賦值
enum OprateEnumTwo{
START,SECOND=3,THREE,END
}
//將中間值 設為3 看ts為其他值賦值多少
console.log("start..."+OprateEnumTwo.START)
console.log("second..."+OprateEnumTwo.SECOND)
console.log("three..."+OprateEnumTwo.THREE)
console.log("end..."+OprateEnumTwo.END)
執行結果
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-QqSgEfEW-1612158764822)(./img/2021-01-04_134838.png)]
可以發現,還是從0開始,但是 因為我們給SECOND賦值了3,SECOND以後的列舉值,以她為起點自增
列舉類屬性不能用數字命名
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-KnW91ejz-1612158764822)(./img/2021-01-04_135746.png)]
與上面的數字值列舉不同,如果你給列舉賦值了字串,那該列舉屬性之後的屬性,必須賦值
當我們去掉T3 T4時,檢視執行結果
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-P94zqQCn-1612158764822)(./img/2021-01-04_140223.png)]
TS的列舉型別,要特別主要自動賦值,自增這個特點
註解
TS提供的高階工具類
- type : 用來接收ts的型別,可以接收一個新的型別,也可以其他ts型別建立別名,別名不會新建一個型別,而是建立一個新名字來引用此型別(一個別名能對應多種型別),type 就是用來接收TS型別的
//程式碼中臨時建立一個新的型別,用type物件接受
//NewType 是程式碼中臨時定義的物件
type NewType = {
code?:number,
name?:string
};
let typeObj : NewType = {
code:12,
name:"sdad"
};
//為已有型別建立別名
//NewCommonResultData是CommonResultData別名,實際作用和CommonResultData一樣
type NewCommonResultData = CommonResultData;
let newData : NewCommonResultData = {
code:10,
msg:"aass",
data:null
};
// type接收聯合型別,對應多種型別
type DataOrStr = string | CommonResultData;
let dataOrStr : DataOrStr = "aaa";
dataOrStr = {
code:10,
msg:"aass",
data:null
};
- Partial : 產生新的型別給T所有屬性加上?,變為可選
type stu = {
readonly name:string,//只讀,定義後此屬性不能修改
sex:string,//屬性 ? 代表是否可為underfined,不要? 則接受值時,必須全部使用
level:number
};
let stuObj : stu = {name:"武漢",sex:"女",level:7};
//newStr 和 stu 互不影響,newStr時基於stu新的型別
type newStr = Partial<stu>;//Partial 產生新的型別給所有屬性加上?,變為可選
let student : newStr = {level:1};//編譯不報錯,name和sex都可以不定義
- Required : Required 與Partial正好相反,使得所有屬性,必須定義
- Readonly : 給T所有屬性都加上readonly 屬性,使其定義後無法修改
type readOnlyStu = Readonly<newStr>;
let readOnlyS : readOnlyStu = {name:"不可能變更"};
//readOnlyS.name = "aaa";//ReadOnly產生的type不能被修改
//readOnlyS.level = 2;//所有屬性都不能被修改
- Pick<T,K extends keyof T> : 從T中挑選部分屬性生成新的type
type halfStu = Pick<stu,"sex"|"level">;//Pick 從stu中,拿出部分屬性,產生一個新的type
let halfStuObj : halfStu = {sex:"nan",level:1};
- Omit<T,K extends keyof T>: 刪除T部分屬性,產生一個新的type,與Pick正好相反
type delStu = Omit<stu,"sex">;//Omit 刪除部分屬性,產生一個新的type,與Pick正好相反
//let delStuObj : delStu = {sex:"a"};//sex被刪除,編譯報錯
- Extract<T, U>:作用是從 T 中提取出 U
type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
type T1 = Extract<string | number | (() => void), Function>; // () => void
- Exclude<T, U>:將 T 中某些屬於 U 的型別移除掉。
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number
- Record<T,K extends keyof T>
Record使用效果如下
type petsGroup = 'dog' | 'cat' | 'fish';
interface IPetInfo {
name:string,
age:number,
}
type pet = Record<petsGroup,IPetInfo>;
let petObj : pet = {
dog : {
name:'dog',
age:5
},
cat : {
name:'cat',
age:9
},
fish : {
name:'fish',
age:2
}
}
IEMP-SXTL-BFF專案程式碼解析
專案啟動環境
- 安裝nodejs
- 更新配置npm
- 專案部分依賴需要python和c++類庫,安裝python,vs_community安裝c++桌面開發工具
- npm run dev 啟動專案開發環境應用
Koa基於nodeJs的Web框架
服務啟動入口
src\index.ts 是整個伺服器程式的啟動入口
const app = await appInit.init();//呼叫src\app.ts的init方法,主要返回Koa例項
// 獲取Koaservice執行埠號
const serverPort = normalizePort(String(commonCfg.bffConfig.port) || '3000');//埠配置於src\config
//nodeJs建立一個伺服器的程式碼
const server = http.createServer(app.callback());
//事件掩飾設定 0 立即執行
server.setTimeout(0);
//繫結伺服器埠
server.listen(serverPort);
server.on('error', onError);
server.on('listening', onListening);
koa app設定
export default {
async init(): Promise<Koa> {
//定義一個Koa 服務端例項
const app = new Koa();
// 全域性變數初始化
globInit();
// 全域性錯誤資訊處理
app.use(globalError);
// 全域性token校驗
// app.use(checkToken(AuthService.checkToken));
// spring監控
app.use(actuator());
// 重放攻擊保護
// if (commonCfg.env !== 'dev') {
// app.use(replayAttack(['swagger', 'actuator', '/system', 'example']));
// }
// 日誌
app.use(koaLogger());
// 請求資訊列印
app.use(requestInfo);
// 資料壓縮
app.use(
koaCompress({
threshold: 2048,
flush: require('zlib').constants.Z_SYNC_FLUSH,
}),
);
// 格式化返回值
app.use(resFormatter);
// 初始化框架
initFrame(
app,
{},
{
authorizationChecker,
},
);
// eureka註冊
if (commonCfg.registerToEureka) {
eureka.start(async () => {
await initData.init();
});
} else {
await initData.init();
}
return app;
},
};
主要定義了 伺服器端例項
const app = new Koa();
app.use(引數:方法):只能用於註冊中介軟體並且必須是生成器函式
app.use(function)
路由設定
src\inits\initFrame.ts中定義了koa-router及其配置,及外部訪問程式的設定
const defaultRoutingControllersOptions: RoutingControllersOptions = {
//controller層包路徑,於javacontroller的邏輯一致
controllers: [`${baseDir}/controllers/**/*{.js,.ts}`],
//middlewares包路勁,配置了koa-actuator 應該是永瀨監控程式執行情況的
middlewares: [`${baseDir}/middlewares/**/*{.js,.ts}`],
//url字首
routePrefix: '/bff/v1',
validation: true,
cors: true,
defaultErrorHandler: false,
};
// koa app設定路由規則
koa.use(router.routes());
編寫對外提供的服務(controller + service)
如上,我們定義了一個web伺服器的例項,埠,url字首,接下來編寫程式,為服務提供功能
在controllers包下定義controller類:TttareController
/**
* @Description:
* @author: 測試ts編寫的controller層
* @date: 2020/01/02
*/
import { Inject } from 'typedi';
import { Body, Get, JsonController, Param, Post, Put, Authorized, Ctx } from 'routing-controllers';
import { ApiOperation, ApiTag } from '../lib/routing-controllers-openapi';
import CreateUserBody from '../definitions/CreateUserBody';
import { CustomError } from '../common/CustomError';
import UserDataManager from '../biz/example/UserDataManager';
@ApiTag('示例')
@JsonController('/tttare')
export default class TttareController {
@Inject()
private userDataInstance: UserDataManager;//ts注入
@ApiOperation('測試controller的路由和注入', '測試')
@Get('/noParam')
getTestData() {
return this.userDataInstance.getTestData();
}
@Get('/getById/:id')//rest風格 url傳參
getOne(@Param('id') id: number) {
return { name: `User #${id}` };
}
//http://localhost:3001/bff/v1/tttare/getById/12
//請求返回
//{
// "name": "User #12"
//}
@ApiOperation('測試controller的路由和注入,呼叫引數有返回值', '測試')
@Post('/param')
getTestDataWithParam(@Ctx() ctx,//ctx是context的縮寫
@Body()
queryParam: any) {
console.log(ctx);
console.log(queryParam);
return this.userDataInstance.getTestDataWithParam(queryParamList);
}
}
@Ctx() ctx是context的縮寫中文一般叫成上下文,這個在所有語言裡都有的名詞,可以理解為上(request)下(response)溝通的環境,所以koa中把他們兩都封裝進了ctx物件,koa官方文件裡的解釋是為了呼叫方便,ctx.req=ctx.request,ctx.res=ctx.response,類似linux系統中的軟連線?最終執行還是request和response物件
// log ctx物件如下
{ request:
{ method: 'POST',
url: '/bff/v1/tttare/param',
header:
{ 'content-type': 'application/json',
'user-agent': 'PostmanRuntime/7.24.0',
accept: '*/*',
'cache-control': 'no-cache',
'postman-token': 'aecaa0bc-108c-4a4e-8535-d36304b21b61',
host: 'localhost:3001',
'accept-encoding': 'gzip, deflate, br',
connection: 'keep-alive',
'content-length': '35' } },
response:
{ status: 404,
message: 'Not Found',
header: { vary: 'Accept-Encoding, Origin' } },
app: { subdomainOffset: 2, proxy: false, env: 'dev' },
originalUrl: '/bff/v1/tttare/param',
req: '<original node req>',
res: '<original node res>',
socket: '<original node socket>' }
@Body() queryParam: any body是請求體,接收請求的json字串,將json字串轉為js物件
//log queryParam物件如下
[ { 'name ': 'tttare', sex: '男', level: 2 },
{ 'name ': 'tom', sex: '男', level: 3 },
{ 'name ': 'mick', sex: '男', level: 2 },
{ 'name ': 'nancy', sex: '女', level: 2 } ]
專案使用typedi框架實現依賴注入
//controller層引入 Inject依賴注入service
import { Inject } from 'typedi';
@Inject()
private userDataInstance: UserDataManager;//ts注入
/**********************************/
//service使用 Service做控制反轉,將UserDataManager例項的產生交個框架
import { Service } from 'typedi';
@Service()
export default class UserDataManager
編寫service,注入controller層呼叫
src\biz\example\UserDataManager.ts的ts編寫邏輯處理
//給學生物件按年級 level 分類
public getTestDataWithParam(queryParamList: any[]):Map<number,any[]>{
//ts提供的Map type,和java一樣,key-value儲存
let map = new Map<number,any[]>();
//利用map的api進行學生分組
queryParamList.forEach(item=>{
if(map.has(item.level)){
map.get(item.level).push(item)
}else{
map.set(item.level,[item]);
}
})
return map;
}
測試編寫的介面
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-Qxu9WBw5-1612158764823)(./img/2021-01-03_143335.gif)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-s2QTQhi2-1612158764823)(./img/2021-01-03_143507.gif)]
斷點除錯
在vscode介面找到debugger視窗,點選驅動專案
debugger專案啟動埠和dev埠一致,要先關了dev環境[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-0m5dE7Nr-1612158764824)(./img/2021-01-03_143813.gif)]
debugger啟動配置
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-emhlYz9g-1612158764824)(./img/2021-01-03_144433.gif)]
debugger過程[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-gyzBrA78-1612158764825)(./img/2021-01-03_144118.gif)]
開發常見API解析-進階
非同步相關(async await Promise)
非同步操作一般是在主執行緒中,開闢新的執行緒去做耗時操作,專案中最多的便是網路請求;
- async 修飾方法,代表此方法方法體內有非同步操作,且改方法的返回值會被包裝成一個Promise ,預設將方法的返回值作Promise中 resolve方法的引數
- await 只能存在與被async修飾的方法體內,來修飾一個非同步呼叫操作(或者說修飾一個返回Promise的方法),用來暫停主執行緒,等待非同步呼叫完成,主執行緒才開始往下走,感覺是將原先的非同步操作,變為了同步一的一種效果,一般是因為主執行緒下面的操作需要非同步呼叫(網路請求)的返回值;(await 所取的引數來自於Promise中resolve函式的賦值,也就是請求成功時Promise呼叫的resolve方法)
- Promise 是為非同步程式設計提供了一種解決方案
名稱:Promise,譯為“承諾”,這也就表達了將來會執行的操作,代表非同步操作;
狀態:一共有三種狀態,分別為pending(進行中)、fulfilled(已成功)和rejected(已失敗)。
特點:(1)只有非同步操作可以決定當前處於的狀態,並且任何其他操作無法改變這個狀態;
(2)一旦狀態改變,就不會在變。狀態改變的過程只可能是:從pending變為fulfilled和從pending變為rejected。如果狀態發生上述變化後,此時狀態就不會在改變了,這時就稱為resolved(已定型)
Promise使用例項
public static testMethod(){
//Promise 如何實現非同步操作
//將非同步操作包裝入Promise物件的 (resolve, reject) => {}方法中
const promise = new Promise((resolve, reject) => {
let error = '連線失敗';
let value ="請求成功";
let success = false;
if (success) {
resolve(value); //pending => fulfilled
} else {
reject(error); // pending => rejected
}
});
//如何獲取非同步操作的執行結果
promise.then((v)=>{
//then 獲取到 resolve(value) 方法的引數 value
console.log(`then:${v}`)
}).catch((e)=>{
//catch 獲取 reject(error) 方法的引數 error
console.log(`catch:${e}`)
}).finally(()=>{
//finally 必須執行的處理
console.log("處理結束")
})
return promise
}
//呼叫testMethod方法
@post("/forTest")
async fotTest(ctx){
console.log("start...")
AccountService.testMethod();
console.log("end...")
}
執行結果
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-NxiFzULu-1612158764825)(./img/2021-01-20_104954.png)]
如上圖所示,Promise物件已經將我們的操作變為非同步操作了
async 標記方法例項
//方法和簡單,async能不能讓方法非同步
public static async testMethod2(){
console.log("async方法執行中")
return "aaa"
}
@post("/forTest")
async fotTest(ctx){
console.log("start...")
//async 的返回值只能用Promise物件接收
let p:Promise<any> = AccountService.testMethod2();
p.then((v)=>{
//then中操作方法的返回值
console.log("取到asyn方法返回值"+v)
})
console.log("end...")
}
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-fSdjaJFW-1612158764825)(./img/2021-01-20_110404.png)]
如上圖所示,async標記的方法,方法體並不是非同步的,非同步操作需要Promise來實現,new Promise和Promise的then,catch,finally是非同步的;故async更多是標記方法存在非同步操作,並使得方法的返回值被Promise包裝
IBS-BFF專案非同步操作例項(async await Promise的組合運用)
//RedisUtil中的hgetall方法,講操作redis非同步進行
async hgetall(key): Promise<any> {
return new Promise((resolve, reject) => {
client.hgetall(key, (err, res) => {
//成功與失敗都呼叫resolve,是為了方便用 await取值
if (err) {
resolve(false);
} else {
resolve(res);
}
});
});
}
//await 修飾的async方法,會直接拿到方法返回值中的 resolve引數
//await redisUtil.hgetall 可知,data為Promise resolve方法的引數,及預設取呼叫成功的返回值
const data = await redisUtil.hgetall(this.inSettleAccountKey);
if (data !== false) {
for (const key in data as any) {
if (data.hasOwnProperty(key)) {
if (new Date().getTime() - parseFloat(data[key]) >= this.expiredTime) {
await this.deleteAccountCalData(parseInt(key), true);
}
}
}
} else {
G.logger.error('賬戶快取資料讀取出錯');
}
//await Promise.all引數是一個非同步方法集合,dataList則為一個數據,
//長度為方法集合長度,dataList的集合對應的是每個非同步方法的返回值
//all將多個非同步操作放在一起成為一個新的Promise,如果都成功,則返回長度相同的返回值陣列
//一旦有一個失敗,則返回第一個失敗操作的reject狀態值,
//有點事務的意思,集合內的非同步方法要麼都成功,只要有一個失敗,則認為他們都失敗
const dataList = await Promise.all([
MeterInfoQuery.queryLineShareSchemeAccountMapData(),
this.queryLineGatewayMapData(lineIDList),
MeterInfoQuery.queryLineAccountDataMap(lineIDList),
]);
裝飾器和註解
裝飾器(Decorator):僅提供定義劫持,能夠對類及其方法、方法入參、屬性的定義並沒有提供任何附加元資料的功能。
註解(Annotation):僅提供附加元資料支援,並不能實現任何操作。需要另外的 Scanner 根據元資料執行相應操作。
TS中,註解和裝飾器是可以說是一個東西,缺一不可,註解提供元資料,即其可以通過修飾類和方法、引數上,為裝飾器提供元資料(類資訊,方法資訊,引數,以及註解本身的入參),而裝飾器則負責實現對元資料 進行功能的擴充套件;
@controller('/account')
export default class AccountController extends BaseController {
~ ~ ~ ~ ~ ~ ~ ~
}
以TS Web服務開發常用的@controller註解為例子,其定義如下
export declare const controller: (path?: string) => ClassDecorator;
註解將自身的引數 path傳遞給了ClassDecorator 類裝飾器,而類處理器本身是一個Function
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
typedi框架的AOP和IOC
@Service()
export default class UserDataManager {
~~~~~~
}
@JsonController('/users')
export default class UsersController {
@Inject()
private userDataInstance: UserDataManager;
}
//TODO
常用工具類
- moment 時間日期操作工具類
import * as moment from 'moment';
const startTime = moment().format('YYYY-MM-DD HH:mm:ss');//2021-01-20 16:42:20 當前時間字串形式
const endTime = moment()
.add(1, 'd')//'d' 時間單位: 天
.format('YYYY-MM-DD 00:00:00');//2021-01-21 00:00:00 當前時間加一天
//將指定時間戳轉成時間字串
const startTime2 = moment(1609430400000).format('YYYY-MM-DD HH:mm:ss');//2021-01-01 00:00:00
//moment 的時間單位如下
"year" | "years" | "y" |
"month" | "months" | "M" |
"week" | "weeks" | "w" |
"day" | "days" | "d" |
"hour" | "hours" | "h" |
"minute" | "minutes" | "m" |
"second" | "seconds" | "s" |
"millisecond" | "milliseconds" | "ms"
- lodash( _ ) js常用工具庫
lodash工具類物件用 _ 表示,就像jquery 用 $ 一樣,全部API文件地址https://www.lodashjs.com/docs
下面為專案中對 lodash 工具類的使用例項
let params = [{
name : 'aaa',
age : 12
},{
name : 'bbb',
age : 14
} ,{
name : 'CCC',
age : 11
},{
name : 'ddd',
age : 9
}]
//G._.cloneDeep 深克隆
let newParams = G._.cloneDeep(params);
//G._.get 陣列取值,第一個引數為陣列,第二個為path集合
//path 第一個值為索引,第二個為屬性,類似與麵包屑取值,層層向下取值
let getParams = G._.get(params,[1,"name"]);// bbb
//G._.remove 便利陣列,將滿足條件的元素從陣列中移除
G._.remove(params,param =>{
return param.age > 13
})
//將多維陣列 遞迴為一維陣列
let newArray = G._.flattenDeep([1, [2, [3, [4]], 5]]);//[1,2,3,4,5]
//將陣列按 size=2分組,無法被分割成全部等長的區塊,那麼最後剩餘的元素將組成一個區塊
let chunkArray = G._.chunk(newArray,2);//[[1,2],[3,4],[5]]
//G._.uniq 陣列元素去重
let uniqArr = G._.uniq([1,2,3,3,3,5,5]);//[1,2,3,5]
//對長度小於2的字串,進行前或後的拼接 0 補位,如果長度大於等於2則不處理
let start = G._.padStart("9", 2, '0');//09
let end = G._.padEnd("9", 2, '0');//90
let end2 = G._.padEnd("19", 2, '0');//19
let initList = [[{text:"臥推"}],[{text:"深蹲"}],[{text:"側平舉"}]];
//將多維陣列變成一維陣列
let flatMap = G._.flatMapDeep(initList);//[{text:"臥推"},{text:"深蹲"},{text:"側平舉"}]
//連個陣列元素取交集
let interArr = G._.intersection([1,2,4,5],[2,3,4]);//[2,4]
//集合物件按屬性排序 按age 升序排列
let orderArr = G._.orderBy(newParams, ['age'], ['asc'])
//按欄位分組
const groupData = G._.groupBy(data.data, item => {
return item.operator;
});
return "aaa"
- Map 鍵值對儲存物件
陣列中移除
G..remove(params,param =>{
return param.age > 13
})
//將多維陣列 遞迴為一維陣列
let newArray = G..flattenDeep([1, [2, [3, [4]], 5]]);//[1,2,3,4,5]
//將陣列按 size=2分組,無法被分割成全部等長的區塊,那麼最後剩餘的元素將組成一個區塊
let chunkArray = G..chunk(newArray,2);//[[1,2],[3,4],[5]]
//G..uniq 陣列元素去重
let uniqArr = G..uniq([1,2,3,3,3,5,5]);//[1,2,3,5]
//對長度小於2的字串,進行前或後的拼接 0 補位,如果長度大於等於2則不處理
let start = G..padStart(“9”, 2, ‘0’);//09
let end = G..padEnd(“9”, 2, ‘0’);//90
let end2 = G..padEnd(“19”, 2, ‘0’);//19
let initList = [[{text:“臥推”}],[{text:“深蹲”}],[{text:“側平舉”}]];
//將多維陣列變成一維陣列
let flatMap = G..flatMapDeep(initList);//[{text:“臥推”},{text:“深蹲”},{text:“側平舉”}]
//連個陣列元素取交集
let interArr = G..intersection([1,2,4,5],[2,3,4]);//[2,4]
//集合物件按屬性排序 按age 升序排列
let orderArr = G..orderBy(newParams, [‘age’], [‘asc’])
//按欄位分組
const groupData = G..groupBy(data.data, item => {
return item.operator;
});
return “aaa”
+ Map 鍵值對儲存物件