javascript - 如何延迟函数的执行,JavaScript

标签 javascript node.js

背景

我正在尝试创建一个工厂函数,它以给定的延迟执行特定的异步函数。

出于这个问题的目的,这将是我所指的异步函数:

/*
 *  This is a simulation of an async function. Be imaginative! 
 */
let asyncMock = function(url) {
    return new Promise(fulfil => {

        setTimeout(() => {
            fulfil({
                url,
                data: "banana"
            });
        }, 10000);

    });
};

此函数接受一个 url 并返回一个包含该 URL 和一些数据的 JSON 对象。

在我的代码中,我通过以下方式调用了这个函数:

asyncMock('http://www.bananas.pt')
.then(console.log);

asyncMock('http://www.berries.com')
.then(console.log);

//... badjillion more calls

asyncMock('http://www.oranges.es')
.then(console.log);

问题

这里的问题是所有这些调用都是在同一时间进行的,因此使 asyncMoc 正在使用的资源过载。

目标

为了避免前面的问题,我希望延迟 Xms 对 asyncMoc 的所有调用的执行。

这是我假装的图形:

delayed_requests

为了实现这一点,我编写了以下方法:

  1. 使用 promise
  2. 使用设置间隔

使用 promise

let asyncMock = function(url) {
  return new Promise(fulfil => {

    setTimeout(() => {
      fulfil({
        url,
        data: "banana"
      });
    }, 10000);

  });
};

let delayFactory = function(args) {

  let {
    delayMs
  } = args;

  let promise = Promise.resolve();

  let delayAsync = function(url) {
    return promise = promise.then(() => {

      return new Promise(fulfil => {
        setTimeout(() => {
          console.log(`made request to ${url}`);
          fulfil(asyncMock(url));
        }, delayMs);
      });
    });
  };

  return Object.freeze({
    delayAsync
  });
};

/*
 *  All calls to any of its functions will have a separation of X ms, and will
 *  all be executed in the order they were called. 
 */
let delayer = delayFactory({
  delayMs: 500
});

console.log('running');

delayer.delayAsync('http://www.bananas.pt')
  .then(console.log)
  .catch(console.error);

delayer.delayAsync('http://www.fruits.es')
  .then(console.log)
  .catch(console.error);

delayer.delayAsync('http://www.veggies.com')
  .then(console.log)
  .catch(console.error);

这个工厂有一个叫做delayAsync的函数,它将所有对asyncMock的调用延迟500ms。但是,它也强制嵌套执行等待前一个结果的调用 - 这不是故意的。

这里的目标是在 500 毫秒内对 asyncMock 进行三次调用,并在 10 秒后收到三个响应,相差 500 毫秒。

使用设置间隔

在这种方法中,我的目标是拥有一个具有参数数组的工厂。然后,每隔 500 毫秒,计时器将运行一个执行器,该执行器将从该数组中获取一个参数并返回一个结果:

/*
 *  This is a simulation of an async function. Be imaginative! 
 */
let asyncMock = function(url) {
  return new Promise(fulfil => {

    setTimeout(() => {
      fulfil({
        url,
        data: "banana"
      });
    }, 10000);

  });
};


let delayFactory = function(args) {

  let {
    throttleMs
  } = args;

  let argsList = [];
  let timer;

  /*
   *  Every time this function is called, I add the url argument to a list of 
   *  arguments. Then when the time comes, I take out the oldest argument and 
   *  I run the mockGet function with it, effectively making a queue.
   */
  let delayAsync = function(url) {
    argsList.push(url);

    return new Promise(fulfil => {

      if (timer === undefined) {

        console.log('created timer');
        timer = setInterval(() => {

          if (argsList.length === 0) {
            clearInterval(timer);
            timer = undefined;
          } else {
            let arg = argsList.shift();

            console.log('making  request ' + url);
            fulfil(asyncMock(arg));
          }
        }, throttleMs);

      } else {
        //what if the timer is already running? I need to somehow 
        //connect it to this call!
      }
    });
  };



  return Object.freeze({
    delayAsync
  });
};

/*
 *  All calls to any of its functions will have a separation of X ms, and will
 *  all be executed in the order they were called. 
 */
let delayer = delayFactory({
  delayMs: 500
});

console.log('running');

delayer.delayAsync('http://www.bananas.pt')
  .then(console.log)
  .catch(console.error);

delayer.delayAsync('http://www.fruits.es')
  .then(console.log)
  .catch(console.error);

delayer.delayAsync('http://www.veggies.com')
  .then(console.log)
  .catch(console.error);
// a ton of other calls in random places in code

这段代码更糟糕。它执行 asyncMoch 3 次,没有任何延迟,始终使用相同的参数,然后因为我不知道如何完成我的 else 分支,它什么也不做。

问题:

  1. 哪种方法更适合实现我的目标,如何解决?

最佳答案

我假设您希望 delayAsync 返回的 promise 基于 asyncMock 的 promise 来解决。

如果是这样,我会使用基于 promise 的方法并像这样修改它(见评论):

// Seed our "last call at" value
let lastCall = Date.now();
let delayAsync = function(url) {
  return new Promise(fulfil => {
    // Delay by at least `delayMs`, but more if necessary from the last call
    const now = Date.now();
    const thisDelay = Math.max(delayMs, lastCall - now + 1 + delayMs);
    lastCall = now + thisDelay;
    setTimeout(() => {
      // Fulfill our promise using the result of `asyncMock`'s promise
      fulfil(asyncMock(url));
    }, thisDelay);
  });
};

这确保了对 asyncMock 的每次调用在前一个调用之后至少有 delayMs(由于计时器的变化,给或少一毫秒),并确保第一个是延迟至少 delayMs

带有一些调试信息的实例:

let lastActualCall = 0; // Debugging only
let asyncMock = function(url) {
  // Start debugging
  // Let's show how long since we were last called
  console.log(Date.now(), "asyncMock called", lastActualCall == 0 ? "(none)" : Date.now() - lastActualCall);
  lastActualCall = Date.now();
  // End debugging
  return new Promise(fulfil => {

    setTimeout(() => {
      console.log(Date.now(), "asyncMock fulfulling");
      fulfil({
        url,
        data: "banana"
      });
    }, 10000);

  });
};

let delayFactory = function(args) {

  let {
    delayMs
  } = args;

  // Seed our "last call at" value
  let lastCall = Date.now();
  let delayAsync = function(url) {
    // Our new promise
    return new Promise(fulfil => {
      // Delay by at least `delayMs`, but more if necessary from the last call
      const now = Date.now();
      const thisDelay = Math.max(delayMs, lastCall - now + 1 + delayMs);
      lastCall = now + thisDelay;
      console.log(Date.now(), "scheduling w/delay =", thisDelay);
      setTimeout(() => {
        // Fulfill our promise using the result of `asyncMock`'s promise
        fulfil(asyncMock(url));
      }, thisDelay);
    });
  };

  return Object.freeze({
    delayAsync
  });
};

/*
 *  All calls to any of its functions will have a separation of X ms, and will
 *  all be executed in the order they were called. 
 */
let delayer = delayFactory({
  delayMs: 500
});

console.log('running');

delayer.delayAsync('http://www.bananas.pt')
  .then(console.log)
  .catch(console.error);

delayer.delayAsync('http://www.fruits.es')
  .then(console.log)
  .catch(console.error);

// Let's hold off for 100ms to ensure we get the spacing right
setTimeout(() => {
  delayer.delayAsync('http://www.veggies.com')
    .then(console.log)
    .catch(console.error);
}, 100);
.as-console-wrapper {
  max-height: 100% !important;
}

关于javascript - 如何延迟函数的执行,JavaScript,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42651439/

相关文章:

javascript - 通过引用传递函数

javascript - jQuery Ajax - 仍在重新加载页面 - Laravel 分页

javascript - 使用 Cordova/jQuery-mobile 在外部设备浏览器中打开链接

node.js - POST 请求在 Nodejs Expressjs MVC 应用程序中仅工作一次

javascript - 当页面居中时,带有输入字段的 jQuery-ui slider 句柄跟随不起作用

javascript - 当项目停止在 .NET 中运行时,是否有办法阻止 Web 方法工作?

javascript - Node.js 将相同的可读流传输到多个(可写)目标

node.js - 在 Node js中使用pdfkit指定PDF页面大小

javascript - 编辑嵌入在 Discord Music Bot 中的 'Now Playing'

json - Mongoose 返回空对象,在 mongo shell 中工作