跳转到内容

流程工具库

流程工具库提供了基础的流程管理控制功能。

运行流程

在Javascript语言中,运行一段流程代码可以有如下写法:

常规写法:

ts
// 流程中常涉及异步操作,因此定义为异步函数
async function flow () {
    // 具体流程代码
}

// 执行流程
flow();

极测推荐写法:

ts
// 执行流程
jc.invoke(async () => {
    // 具体流程代码
    jc.exit(); // 结束当前流程
});

ts
// 执行流程
jc.main(async () => {
    // 具体流程代码
});

推荐采用jc.invoke()jc.main()来定义并执行流程代码,可以显著提高代码的可读性。

jc.invoke()

调用一个传入的函数,并支持传入该函数的调用参数。例如在定义1个箭头函数的同时调用它,相比其它写法,使用本函数有助于让代码更有可读性。1个典型的应用场景是,在脚本代码的顶层内容中定义 & 执行1个异步(async)函数,以便在其中使用await来同步调用其它异步函数。

  • 函数签名
ts
function invoke <Fn extends (...args: any[]) => any> (fn: Fn, ...args: Parameters<Fn>): ReturnType<Fn>;
  • 输入参数

    • fn 待调用的函数
    • args 调用时的输入参数
  • 返回值:fn的返回值

  • 代码示例

ts
jc.invoke(async () => {
    await jc.sleepAsync(1000);
    jc.exit();
})

jc.main()

调用传入的函数,并在其运行结束后、用其返回结果来结束流程运行。本函数是个便利函数,内置了对jc.exit()等函数的调用操作,简化流程编写。实际上,本函数基本等价于如下代码实现:

ts
const main = async (fn: () => any) => jc.exit(await jc.invoke(fn));
  • 函数签名
ts
function main (fn: () => any): void
  • 输入参数

    • fn 待调用的函数。类似于C/C++Java等编程语言中的main()函数,支持同步、异步的函数。其返回结果将作为流程结束状态并传给exit()函数。
  • 返回值:无

  • 代码示例

ts
// 流程启动后,休眠1秒钟,然后结束
jc.main(() => jc.sleepAsync(1000))

退出流程

由于极测采用了高度开放的脚本运行机制,允许脚本自主控制执行过程,并且难以实时监测脚本的运行状态。例如,脚本可能启动异步任务(如I/O操作或定时任务等)。当脚本运行返回时,不能据此推断脚本已完全终止运行。因此,脚本的执行终止只能通过调用 jc.exit() 来实现。

jc.exit()

结束当前流程,包含其所有子流程。但不会影响其父流程。

调用本函数之后,会触发当前流程的清理操作,包括结束其所有子流程、结束其开启的所有会话,以及特定模块自定义的清理函数,等等。

本函数返回之后,当前脚本不能 & 不应再执行其它操作,否则可能触发异常。

  • 函数签名
ts
function exit (result?: string): void
  • 输入参数

    • result 结束状态,可选。若省略或传入「空字符串」将被视为正常结束,否则视为异常/错误结束。该状态将作为结束信息记录到当前流程 及其 所有子流程。但只有根流程的信息会保存到数据库中。
  • 返回值:无

  • 代码示例

ts
jc.invoke(async () => {
    // 休眠1秒钟
    await jc.sleepAsync(1000);

    // 正常结束
    jc.exit();
})

流程复用

通过调用jc.importWith()可以在当前流程代码中插入并执行其它页面的流程代码。

jc.importWith()

  • 函数签名
ts
function importWith (pageName: string): void
  • 输入参数
    • pageName 流程页面的名称,本函数将在当前空间中的所有「流程页面」中查找该名称对应的页面,
      • 查找操作仅限流程页面,其余类型的页面都将被忽略,即使存在相同名称的页面,
      • 若未找到指定名称的页面,或该页面的脚本为空,则子模块不会被实际执行,
      • 若找到多个符合条件的页面,则始终使用按默认排序后的第1个页面,
  • 返回值:无

当函数返回后,指定流程(若存在)的执行结果将在当前流程的后续代码中使用。例如,可以调用导入流程中定义的函数等。

代码示例:

js
js.main(() => {
    jc.importWith('模块示例1');
 
    return jc.sleepAsync(1000);
})

运行子流程

极测中的「流程」很大程度上可以类比为操作系统中的「进程」,因此「子流程」可以类比为「子进程」,即每个流程及其子流程具有隔离的、独立的资源空间,例如打开的协议接口等。当某个流程结束退出时(调用jc.exit()),会先结束其所有子流程,然后结束该流程自己。但若某个子流程结束时,不会影响其兄弟流程 & 父流程的运行。这与进程的结束方式类似。

jc.run()

创建子流程,执行传入的流程代码。

  • 函数签名
ts
function run (script: string): void
  • 输入参数
    • script 字符串,待运行的流程代码
  • 返回值:无

代码示例

ts
jc.main(() => {
    jc.run('jc.info("子流程已运行");')

    return jc.sleepAsync(1000);
})

jc.runWith()

创建子流程,执行指定页面的流程代码。

  • 函数签名
ts
function runWith (pageName: string): void
  • 输入参数
    • pageName 流程页面的名称,本函数将在「当前空间」中的所有「流程页面」中查找该名称对应的页面,
      • 查找操作仅限流程页面,其余类型的页面都将被忽略,即使存在相同名称的页面,
      • 若未找到指定名称的页面,或该页面的脚本为空,则子流程都不会被实际启动,
      • 若找到多个符合条件的页面,则始终使用按默认排序后的第1个页面,
  • 返回值:无

代码示例:

js
js.main(() => {
   jc.runWith('流程示例1');

   return jc.sleepAsync(1000);
})

流程动态

以日志分级的方式提供流程动态的记录接口。

jc.info()

  • 函数签名
ts
function info (message: string): void
  • 输入参数
    • message 字符串,「通知」级别的流程动态信息
  • 返回值:无

代码示例

ts
jc.main(() => {
    jc.info('流程已运行');

    return jc.sleepAsync(1000);
})

jc.warn()

  • 函数签名
ts
function warn (message: string): void
  • 输入参数
    • message 字符串,「告警」级别的流程动态信息
  • 返回值:无

代码示例

ts
jc.main(() => {
    jc.warn('流程异常');

    return jc.sleepAsync(1000);
})

jc.error()

  • 函数签名
ts
function error (message: string): void
  • 输入参数
    • message 字符串,「错误」级别的流程动态信息
  • 返回值:无

代码示例

ts
jc.main(() => {
    jc.error('流程错误');

    return jc.sleepAsync(1000);
})

数据编解码

在数据通信过程中,数据对象会经过编解码转换成 Buffer 对象。用户可以通过调用jc.decodeAs()Buffer 对象还原成数据对象;也可以通过调用jc.encodeFromAsync()将指定页面的某个数据对象转换成 Buffer 对象。

jc.decodeAs()

  • 函数签名
ts
function decodeAs (payload: Buffer, segmentName: string): FlowDecodedSegment | undefined
  • 输入参数
    • payload Buffer对象,数据完成编码后的字节流
    • segmentName 字符串,数据段名称,数据段定义了解码的数据对象格式
  • 返回值:FlowDecodedSegment,解码后的数据对象类型,解码失败返回 undefined

代码示例

ts
jc.main(() => {
    const payload = Buffer.from([0xCA, 0xAA]);
    const data = jc.decodeAs(payload, '示例数据段');

    return jc.sleepAsync(1000);
})

jc.encodeFromAsync()

  • 函数签名
ts
function encodeFromAsync (pageName: string, contentName: string): Promise<Buffer | undefined>
  • 输入参数
    • pageName 流程页面的名称,本函数将在「当前空间」中的所有「流程页面」中查找该名称对应的页面,
      • 查找操作仅限流程页面,其余类型的页面都将被忽略,即使存在相同名称的页面,
      • 若未找到指定名称的页面,或该页面的脚本为空,则子流程都不会被实际启动,
      • 若找到多个符合条件的页面,则始终使用按默认排序后的第1个页面,
    • contentName 字符串,数据名称,本函数将在指定页面中查询符合名称的数据对象
  • 返回值:编码后的Buffer对象,编码失败返回 undefined

代码示例

ts
jc.main(() => {
    const payload = jc.encodeFromAsync('页面1', '数据1');

    return jc.sleepAsync(1000);
})

其他

jc.sleepAsync()

异步函数,休眠指定的时间。

  • 函数签名
ts
type FlowTimeUnit = "ms" | "millis" | "millisecond" | "s" | "sec" | "second" | "m" | "min" | "minute" | "h" | "hour";

function sleepAsync (time: number, unit?: FlowTimeUnit): Promise<void>
  • 输入参数

    • time 待休眠的时间值,其单位由unit参数指定。
    • unit 时间单位,可选,默认值为"ms"。可选值包括:
      • 'ms' | 'millis' | 'millisecond',毫秒,
      • 's' | 'sec' | 'second',秒,
      • 'm' | 'min' | minute',分钟,
      • 'h' | 'hour',小时,
  • 返回值:异步对象,待指定时间到达以后解析完成

  • 代码示例

ts
// 流程启动后,休眠1秒钟,然后结束流程
jc.main(() => jc.sleepAsync(1000))