1. 程式人生 > 資訊 >阿爾法酷推出 3 款企業級散熱風扇:支援卡住保護,最高 1.5 萬 RPM

阿爾法酷推出 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 鍵值對儲存物件