如何延迟执行功能,JavaScript [英] How to delay execution of functions, JavaScript

查看:42
本文介绍了如何延迟执行功能,JavaScript的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图创建一个工厂函数,该函数以给定的延迟执行特定的异步函数.

I am trying to create a factory function that executes a specific async function with a given delay.

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

For the purposes of this question, this will be the async function I refer to:

/*
 *  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对象.

This function takes an url and it returns a JSON object containing that URL and some data.

在我的代码周围,我以以下方式调用了此函数:

All around my code, I have this function called in the following way:

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 的调用的执行.

To avoid the previous problem, I wish to delay the execution of all calls to asyncMoc by Xms.

这是我假装的图形:

为此,我编写了以下方法:

To achieve this I wrote the following approaches:

  1. 使用承诺
  2. 使用setInterval

使用承诺

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 的调用延迟500毫秒.但是,它也强制执行嵌套等待上一个结果的调用-这是不希望的.

This factory has a function called delayAsync that will delay all calls to asyncMock by 500ms.However, it also forces the nest execution of the call to wait for the result of the previous one - which in not intended.

这里的目标是在500ms内对 asyncMock 进行三个调用,并在收到三个相差500ms的响应后10s.

The objective here is to make three calls to asyncMock within 500ms each, and 10s after receive three responses with a difference of 500ms.

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

In this approach, my objective is to have a factory which has an array of parameters. Then, every 500ms, the timer will run an executor which will take a parameter from that array and return a result with it:

/*
 *  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

此代码甚至更糟.它执行3次 asyncMoch 而没有任何延迟,总是使用相同的参数,然后因为我不知道如何完成我的 else 分支,所以它什么也不做.

This code is even worse. It executes asyncMoch 3 times without any delay whatsoever, always with the same parameter, and then because I don't know how to complete my else branch, it does nothing.

  1. 哪种方法可以更好地实现我的目标?如何解决?

推荐答案

我要假设您希望 delayAsync 返回的承诺根据 asyncMock 的承诺进行解析代码>.

I'm going to assume you want the promises returned by delayAsync to resolve based on the promises from asyncMock.

如果是这样,我将使用基于诺言的方法并进行如下修改(请参见评论):

If so, I would use the promise-based approach and modify it like this (see comments):

// 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 .

That ensures that each call to asyncMock is at least delayMs after the previous one (give or take a millisecond thanks to timer vagaries), and ensures the first one is delayed by at least delayMs.

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

Live example with some debugging info:

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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆