react-native中有时候需要定时执行一些后台任务,比如定时发送消息,定时统计数据等.这个时候就需要使用另外注册一个视图来做这些东西了,不然在同步任务中做这些任务,一旦任务比较耗时就会卡住后面的进程,甚至导致后面的进程不再渲染页面.

注册另外一个入口

在合适的位置:页面启动或者某个需要的时候,注册并启动我们的另外一个视图.这个时候其实相当于启动了另外一个线程.

这里注意一下,这个视图里的东西千万不要直接刷新UI相关的数据,所有的内容都应该是内存相关的.

AppRegistry.registerRunnable('RunableTask', TaskRun)

AppRegistry.runApplication('RunableTask', {});

启动定时器

调用init方法,启动我们自己的定时器刷新方法,这里我暂时定义1秒刷新一次,低于1秒间隔的定时器将不在执行.这里也是为了防止部分人设置低间隔的定时器导致APP崩溃.

这里使用react-native内部提供的requestAnimationFrame方法,每次刷新的时候先判断一次是否可以执行任务.

const tasks = new Map();
let currDate = Date.now();
/**
 * 初始化
 */
exports.init = function () {
    global.requestAnimationFrame(run);
}
/**
 * 执行,每秒执行一次
 * 保证秒级工作正确
 */
function run() {
    const now = Date.now();
    if (now > currDate + 1000) {
        currDate = now;
        tasks.forEach(item => item.preStart(now));
    }
    global.requestAnimationFrame(run);
}

任务基类

所有的任务都需要继承这个基类,定时器运行的时候也会通过基类内部的方法去判断是否需要执行方法.这里对外实现了一个timer是时间间隔,单位是毫秒,还有一个start方法用来给每个任务使用的.

通过每次执行preStart方法判断下次执行的时间到了没有来决定是否执行自定义方法.如果到了同时还要计算下一次的时间并存好.

/**
 * 基础工作类
 */
class Jobs {
    name = "base";
    /**
     * 下次执行时间戳
     */
    nextTime = 0;
    /**
     * 重载:时间间隔
     */
    timer = 0;
    /**
     * 预启动
     */
    preStart(now) {
        if (this.timer < 1000) return;
        if (this.nextTime > now) return;
        if (this.nextTime === 0) return this.nextTime = now + this.timer;
        this.nextTime += this.timer;
        this.start(now);
    }
    /**
     * 重载:执行一次设置的方法
     */
    start() { }
}

实现并加入

将设置好的任务加入全局的map示例中,运行的时候会从这个示例中取需要的任务出来.

/**
 * 添加一个工作
 * @param {*} name  名称
 * @param {*} time  时间
 * @param {*} fn    执行函数
 */
const addJob = (name, Job) => tasks.set(name, new Job())

exports.addJob = addJob;

/**
 * 取消任务
 * @param {*} name
 */
const cancelJob = name => tasks.delete(name);

exports.cancelJob = cancelJob;

测试任务

这里模拟一次定时发送统计消息.

另外一个地方实现了将每次需要发送的消息存入内存中,这里只负责取出需要发送的消息.

设置3秒执行一次发送任务,如果数量比较少就修改定时任务的间隔,后面的任务延迟发送.

如果后台接收一次大量的数据,也可以将多次统计合并为一次统计,请求的个数会更少一些.

/**
 * 定时触发统计方法
 */
class TrackJob extends Jobs {
    timer = 3000;
    start() {
        let list = TackList();
        if (list.length > 10) {
            this.timer = 3000;
        } else {
            this.timer = 5000;
        }
        list.forEach(item => {
            request.trackData(item.url, item.data);
            log("埋点事件", item.data);
        })
    }
}
addJob("Track", TrackJob);

更多扩展

在目前的基础上扩展时间间隔的设定方式,实现固定时间日期执行任务.这里可能需要app一直活着才能保证.

加入执行次数,执行超过一定的此时就不再执行.

文章地址

03-05 20:07