Эффективное запуск запланированных задач

алгоритм JavaScript

Такта дела, для достижения цели!

let map = new Map();
function doAction(uid) {
    map.set(uid, new Date().getTime());
}

setInterval(function(){
    for(let uid of map.keys()) {
        if(+new Date() - map.get(uid) > 30000) {
            map.delete(uid);
            console.log(`${uid}超过30s未做任何操作,设置为离线!`);
        }
    }
}, 10000);

Слабые стороны программы:

  • низкая эффективность. Записи, которые были выполнены, все равно будут сканироваться (но не будут отображаться в результирующем наборе), и будет много повторяющихся вычислений;
  • Плохая своевременность. Временная ошибка зависит от интервала опроса, если интервал слишком мал, количество повторных сканирований будет выше, а эффективность станет ниже.

обработка синхронизации

Каждый раз, когда приходит задача, запускается таймер, достигается время таймера и выполняется соответствующая обработка;

function doAction(uid) {
    map.set(uid, new Date().getTime());
    setTimeout(function() {
        console.log(`${uid}超过30s未做任何操作,设置为离线!`);
    }, 30000);
}

Слабые стороны программы:

  • Слишком большое количество таймингов может привести к чрезмерному использованию памяти, что может легко привести к сбоям.

Обработка циклической очереди

структура данных:

  • круговая очередьListLoop, например, вы можете создать слот**круговую очередь**, содержащую 0–30 (по сути, массив);
  • Коллекция задач на каждом кольцеSlot, каждый слот на кольце являетсяSet;
  • Запишите каждую задачу, соответствующую слотуMapсобирать;

Процесс реализации:
первый шаг:0->1->2->3…->29->30->0…CURRORTSLOTINDEX указатель для идентификации вновь обнаруженного через слот;
Шаг 2:
третий шаг:
четвертый шаг:Добавьте uid обратно в новый слот (последний слот, на который указывает указатель CurrentSlotIndex), потому что этот слот будет сканироваться таймером через 30 с.
пятый шаг:Обновите карту и сбросьте значение индекса слота, соответствующего uid

定时任务高效触发

// new Array(31).fill(new Set())
// No,数组中所有Set集合为同一个

let listLoop = new Array(31),
    map = new Map(),  // 记录每个uid的slotIndex
    currentSlotIndex = 1; // 当前要检测的slot

function doAction(uid) {
    // 如果循环队列中已存在该uid,需要先干掉,重新计时
    let slotIndex = map.get(uid);
    slotIndex && listLoop[slotIndex].delete(uid);
    // 将该uid重现添加到循环队列中
    // 周期31,新插入的置入当前的后一个(即,30s后可以扫描到它)
    // 更新map中这个uid的最新slotIndex
    slotIndex = currentSlotIndex - 1;
    listLoop[slotIndex] = listLoop[slotIndex] ? 
        listLoop[slotIndex].add(uid) : new Set().add(uid);
    map.set(uid, slotIndex);
}

// 每秒钟移动一个slot,这个slot对应的set集合中所有uid都为超时
// 如果所有slot对应的set集合都为空,则表示没有uid超时
setInterval(function() {
    var slotSet = listLoop[currentSlotIndex];
    if(slotSet && slotSet.size > 0) {
        for(let uid of slotSet.values()) {
            // 执行完的uid从map集合中剔除
            map.delete(uid);
            console.log(`<${uid}>超过30s未做任何操作,设置为离线!`);
        }
        // 置空该集合
        slotSet.clear();
    }
    // 指标继续+1
    currentSlotIndex = (++currentSlotIndex) % 31;
}, 1000);

// 思路、注意Map集合的内心移除情况。

Преимущества схемы:

  • Нет необходимости повторно опрашивать все заказы, высокая эффективность
  • Без повторного выполнения, один заказ, задание выполняется только один раз
  • Хорошая эффективность, точность до секунды (управление частотой движения таймера может контролировать точность)

Обратитесь к статье:Задача на 10 Вт, как эффективно запустить тайм-аут,1 минута для достижения функции «задержанного сообщения»

учиться по аналогии

Вышеприведенная презентация описывает бизнес-сценарий черезкруговая очередьМы также можем обрабатывать множество подобных сценариев.

  • После того, как заказ программного обеспечения для вызова такси будет завершен, если пользователь не прокомментировал, ему будет автоматически присвоен 5 звезд через 48 часов;
  • Если пользователь продукта данных изменит настройки, они вступят в силу через 1 час;
  • ...