Skip to content

介绍

ChildJob基于node.js子进程实现。触发任务时才会创建进程,任务执行完毕后,必须调用进程退出的方法:Ps.exit(), 否则进程无法退出,造成资源浪费。

优点:随用随取,用完释放,不过多占用电脑资源。

不足:频繁、大量创建任务时,会有启动进程的开销,业务略微延迟,此类业务推荐使用ChildPoolJob。

注意:node.js子进程中无法使用所有涉及electron的api(electron技术本身无法支持),相关业务请写在主进程

目录

./electron/jobs

使用方法

  1. 编写任务代码
javascript
// ./electron/jobs/example/timer.js
'use strict';

const { logger } = require('ee-core/log');
const { isChildJob, exit } = require('ee-core/ps');
const { childMessage } = require('ee-core/message');
const { welcome } = require('./hello');
const { UserService } = require('../../service/job/user');
const { sqlitedbService } = require('../../service/database/sqlitedb');

/**
 * example - TimerJob
 * @class
 */
class TimerJob {

  constructor() {
    this.timer = undefined;
    this.timeoutTimer = undefined;
    this.number = 0;
    this.countdown = 10; // 倒计时
    sqlitedbService.init();
  }

  /**
   * handle()方法是必要的,且会被自动调用
   * params 传递的参数
   */
  async handle() {
    logger.info("[child-process] TimerJob params: ", this.params);
    const { jobId } = this.params;

    // 子进程中使用service
    // 1. 确保引入的 service 中不能有electron 的 api或依赖, electron 不支持
    const userService = new UserService();
    userService.hello('job');

    // 执行任务
    // 多次运行时,重置倒计时
    this.number = 0;
    this.countdown = 10;
    this.doTimer(jobId);

    // sqlite
    const userList = await sqlitedbService.getAllTestDataSqlite();
    logger.info('[child-process] Sqlite userList:', userList);
  }
  
  /**
   * 暂停任务运行
   */
  async pause(jobId) {
    logger.info("[child-process] Pause timerJob, jobId: ", jobId);
    clearInterval(this.timer);
    clearInterval(this.timeoutTimer);
  }

  /**
   * 恢复任务运行
   */
  async resume(jobId, pid) {
    logger.info("[child-process] Resume timerJob, jobId: ", jobId, ", pid: ", pid);
    this.doTimer(jobId);
  }  

  /**
   * 运行任务
   */
  async doTimer(jobId) {
    // 计时器模拟任务
    const eventName = 'job-timer-progress-' + jobId;
    this.timer = setInterval(() => {
      welcome();

      childMessage.send(eventName, {jobId, number: this.number, end: false});
      this.number++;
      this.countdown--;
    }, 1000);

    // 用 setTimeout 模拟任务运行时长
    this.timeoutTimer = setTimeout(() => {
      // 关闭计时器模拟任务
      clearInterval(this.timer);

      // 任务结束,重置前端显示
      childMessage.send(eventName, {jobId, number:0, pid:0, end: true});

      // 如果是childJob任务,必须调用 exit() 方法,让进程退出,否则会常驻内存
      // 如果是childPoolJob任务,常驻内存,等待下一个业务
      if (isChildJob()) {
        exit();
      }
    }, this.countdown * 1000)
  }
}

TimerJob.toString = () => '[class TimerJob]';
module.exports = TimerJob;
  1. 触发任务
javascript
// ./electron/service/example.js

// 引入模块
const { ChildJob } = require('ee-core/jobs');

/**
 * 示例服务(service层为单例)
 * @class
 */
class ExampleService extends Service {

  constructor(ctx) {
    super(ctx);

    // 在构造函数中初始化一些变量
    this.myJob = new ChildJob();
    this.taskForJob = {};
  }

  /**
   * 执行任务
   */ 
  doJob(jobId, action, event) {
    let res = {};
    let oneTask;
    const channel = 'controller/framework/timerJobProgress';
    if (action == 'create') {
      // 执行任务及监听进度
      let eventName = 'job-timer-progress-' + jobId;
      const timerTask = this.myJob.exec('./jobs/example/timer', {jobId});
      timerTask.emitter.on(eventName, (data) => {
        logger.info('[main-process] timerTask, from TimerJob data:', data);
        // 发送数据到渲染进程
        event.sender.send(`${channel}`, data)
      })
    
      // 执行任务及监听进度 异步
      // myjob.execPromise('./jobs/example/timer', {jobId}).then(task => {
      //   task.emitter.on(eventName, (data) => {
      //     Log.info('[main-process] timerTask, from TimerJob data:', data);
      //     // 发送数据到渲染进程
      //     event.sender.send(`${channel}`, data)
      //   })
      // });

      res.pid = timerTask.pid; 
      this.taskForJob[jobId] = timerTask;
    }
    if (action == 'close') {
      oneTask = this.taskForJob[jobId];
      oneTask.kill();
      event.sender.send(`${channel}`, {jobId, number:0, pid:0});
    }
    if (action == 'pause') {
      oneTask = this.taskForJob[jobId];
      oneTask.callFunc('./jobs/example/timer', 'pause', jobId);
    }
    if (action == 'resume') {
      oneTask = this.taskForJob[jobId];
      oneTask.callFunc('./jobs/example/timer', 'resume', jobId, oneTask.pid);
    }

    return res;
  }
}

ExampleService.toString = () => '[class ExampleService]';
module.exports = ExampleService;