Node实现异步过程的状态同步

2018-03-01  本文已影响13人  平仄_pingze

说明

有时会遇到一个耗时异步过程(如转码、慢查询)可能被快速反复调用的情况。如果希望每次调用都能正确返回,而又不希望过程被重复执行消耗资源,可以使用下面的装饰器函数来包装过程。
注意,装饰器可以配置最大同步过程及等待时间,以有限增加和确保回收监听器,使用时应根据情况处理。

装饰器函数:

/**
  * 异步过程(函数)状态同步
  * @param asyncFunc 过程函数
  * @param opts
  *   opts.maxProcess 最大同步过程数
  *   opts.timeout 最大同步等待时间
*/
function stateSynchronize(asyncFunc, opts) {
  opts = opts || {};
  opts.maxProcess = opts.maxProcess || 100;
  opts.timeout = opts.timeout || 60 * 1000;
  const keySet = new Set();
  const emitter = new (require('events').EventEmitter)();
  emitter.setMaxListeners(opts.maxProcess);
  return async function (key, ...args) {
    if (keySet.has(key)) {
      return await (() => {
        return new Promise((resolve, reject) => {
          if (emitter.listeners('finish').length >= opts.maxProcess) {
            return reject(new Error('too much process at the same time'));
          }
          setTimeout(() => {
            return reject(new Error('process wait synchronization timeout'));
          }, opts.timeout);
          emitter.once('finish', ({key, ret}) => {
            return resolve(ret);
          });  
        });
      })();
    }
    keySet.add(key);
    let ret = await asyncFunc(...args);
    keySet.delete(key);
    emitter.emit('finish', {key, ret});
    return ret;
  }
}

测试

// 异步过程
async function work(key) {
  await (function () {return new Promise(resolve => setTimeout(() => resolve(), 2000))})();
  return key;
}

// 装饰
work = stateSynchronize(work);

// 执行
~async function() {
  process.nextTick(async () => {
    let key = 1;
    let ret = await work(key, [key]);
    console.log('1过程执行完毕,返回', ret);
  });

  setTimeout(async () => {
    for (let i=0; i< 11; i++) {
      process.nextTick(async () => {
        let key = 1;
        let ret = await work(key, [key]);
        console.log('2过程执行完毕,返回', ret);
      });
    }
  }, 1000);

  setTimeout(async () => {
    process.nextTick(async () => {
      let key = 2;
      let ret = await work(key, [key]);
      console.log('3过程执行完毕,返回', ret);
    });
  }, 1000);
}();

// 结果中,2过程和1过程有同样的key,虽然2过程执行晚了1s,但仍会和1一起结束,同样返回。
上一篇 下一篇

猜你喜欢

热点阅读