简体   繁体   English

承诺链中承诺之间的延迟

[英]Delays between promises in promise chain

Let's say I am using the following code to run a couple of promises in series:假设我正在使用以下代码连续运行几个 Promise:

let paramerterArr = ['a','b','c','d','e','f']
parameterArr.reduce(function(promise, item) {
  return promise.then(function(result) {
    return mySpecialFunction(item);
  })
}, Promise.resolve())

The code simply calls mySpecialFunction (which returns a promise), waits for the promise to be resolved and then calls mySpecialFunction again etc. So the function is called once for every element in the array, in the correct order.代码简单地调用 mySpecialFunction(它返回一个承诺),等待承诺得到解决,然后再次调用 mySpecialFunction 等等。因此,该函数以正确的顺序为数组中的每个元素调用一次。

How could I make sure that there is a delay of at least 50 milliseconds between every call of mySpecialFunction(item) ?我如何确保每次调用mySpecialFunction(item)之间至少有 50 毫秒的延迟?

It is important that the promises are executed in the right order and the execution time of mySpecialFunction varies every time.以正确的顺序执行承诺很重要,并且mySpecialFunction的执行时间每次都不同。

I guess a synchronous sleep would work, but I'm not planning to run this code in a separate thread, so it would cause annoying ui freezes in the browser.我猜同步睡眠会起作用,但我不打算在单独的线程中运行此代码,因此它会导致浏览器中烦人的 ui 冻结。

I'm not sure if setTimer could somehow be used for this.我不确定 setTimer 是否可以用于此目的。 I mean I can't delay the returning of a promise.我的意思是我不能推迟承诺的回报。

The answers are good, but they wait too long since all the answers wait regardless of whether or not the actual operation took more than 50ms already.答案很好,但它们等待的时间太长,因为无论实际操作是否已经花费了 50 毫秒以上,所有答案都在等待。

You can use Promise.all for it.你可以使用Promise.all

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
let parameterArr = ['a','b','c','d','e','f'];
parameterArr.reduce(function(promise, item) {
  return promise.then(function(result) {
    return Promise.all([delay(50), myPromise(item)]);
  });
}, Promise.resolve());

A really handy utility function to have around is something I call delay() :一个非常方便的实用函数是我称之为delay()东西:

function delay(t, val) {
    return new Promise(function(resolve) {
        if (t <= 0) {
            resolve(val);
        } else {
            setTimeout(resolve.bind(null, val), t);
        }
    });
}

Then, you can use it in a promise chain like this:然后,您可以在这样的承诺链中使用它:

let paramerterArr = ['a','b','c','d','e','f']
parameterArr.reduce(function(promise, item, index) {
  return promise.then(function(result) {
    // no delay on first iteration
    var delayT = index ? 50 : 0;
    return delay(delayT, item).then(mySpecialFunction);
  })
}, Promise.resolve());

You could also make a little utility function for doing the sequential iteration with optional delay:您还可以创建一个小实用函数来执行带有可选延迟的顺序迭代:

// delayT is optional (defaults to 0)
function iterateSerialAsync(array, delayT, fn) {
    if (!fn) {
        fn = delayT;
        delayT = 0;
    }
    array.reduce(function(p, item, index) {
        return p.then(function() {
            // no delay on first iteration
            if (index === 0) delayT = 0;
            return delay(delayT, item).then(fn)
        });
    }, Promise.resolve());
}

And, then you would use it like this:然后你会像这样使用它:

iterateSerialAsync(paramerterArr, 50, mySpecialFunction).then(function(finalVal) {
    // all done here
});

To get a delay of at least 50ms, use Promise.all :要获得至少50 毫秒的延迟,请使用Promise.all

function delay(t) {
  return new Promise(function(resolve) {
    setTimeout(resolve, t);
  });
}
parameterArr.reduce(function(promise, item) {
  return promise.then(function() {
    return Promise.all([
      mySpecialFunction(item),
      delay(50)
    ]);
  });
}, Promise.resolve());

The following shows an example of how you might achieve a promise that doesn't block but waits for a designated time period:下面显示了如何实现不阻塞但等待指定时间段的承诺的示例:

function timedPromise(ms, payload) {
    return new Promise(function(resolve) {
        setTimeout(function() {
            resolve(payload);
        }, ms);
    })
}


var time = Date.now();

timedPromise(1000)
    .then(function() {
        console.log(time - Date.now());
        return timedPromise(2000);
    }).then(function() {
        console.log(time - Date.now());
        return timedPromise(3000);
    });

So, depending on exactly what you want, you should be able to do something like this:因此,具体取决于您想要什么,您应该能够执行以下操作:

let paramerterArr = ['a','b','c','d','e','f']
parameterArr.reduce(function(promise, item) {
  return promise.then(function(result) {
    return mySpecialFunction(item);
  }).then(function(specialResult) {
    return timedPromise(50, specialResult);
  });
}, Promise.resolve())

Here you go: https://jsbin.com/suvasox/edit?html,js,console给你: https : //jsbin.com/suvasox/edit?html,js,console

let paramerterArr = ['a','b','c','d','e','f']
paramerterArr.reduce((p, val) => {
  return p.then(() => {
    return new Promise((res) => {
      setTimeout(() => { res(mySpecialFunction(val)); }, 1000); 
    });
  });
}, Promise.resolve());

p has to be the result of the p.then(). p 必须是 p.then() 的结果。 Only that way you chain the promises.只有这样你才能链接承诺。

Notice, I changed it to 1000ms delay just for emphasis.请注意,我将其更改为 1000 毫秒延迟只是为了强调。

since this seems to be a requirement of mySpecialFunction I'd implement it there.因为这似乎是mySpecialFunction的要求, mySpecialFunction我会在那里实现它。 So that the function delays itself if it is called less than 50ms after the last call如果在最后一次调用后不到 50 毫秒被调用,则该函数会延迟自身

const delayBetweenCalls = (delay, fn) => {
    let lastCall = NaN;
    return function(/*...arguments*/){
        //this and arguments are both forwarded to fn

        return new Promise(resolve => {
            let poll = () => {
                let delta = Date.now() - lastCall;
                if(delta < delay){
                    setTimeout(poll, delta - delay);
                }else{
                    lastCall = Date.now();
                    resolve( fn.apply(this, arguments) );
                }
            }
            poll();
        })
    }
}

then:然后:

const mySpecialFunction = delayBetweenCalls(50, function(some, ...args){
    return someValueOrPromise;
});

//and your loop stays the same:
parameterArr.reduce(function(promise, item) {
    return promise.then(function(result) {
        return mySpecialFunction(item);
    })
}, Promise.resolve())

so it doesn't matter where/how mySpecialFunction is called, there will always be a delay of at least 50ms before it runs the code inside the passed callback.因此无论在何处/如何mySpecialFunction都没有关系,在传递的回调中运行代码之前,总会有至少 50 毫秒的延迟。

Here is my complete solution for delayed promise sequences:这是我针对延迟承诺序列的完整解决方案:


function timeout_sequence_promise(promises = [], timeout = 200) {

    //fake promise used as buffer between promises from params
    const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

    //we need to create array of all promises with delayed buffers
    let delayed_promises = [];

    let total = promises.length;

    let current = 0;

    //every odd promise will be buffer
    while (current < total) {

      delayed_promises.push(promises[current]);
      delayed_promises.push(delay(timeout));

      current++;

    }

    return Promise.all(delayed_promises).then((result) => {

      //we need to filter results from empty odd promises
      return result.filter((item, index) => (index+2)%2 === 0);

    });


  }

It's receive array of promises and timeout delay in ms between them, as parameters.它接收一系列承诺和它们之间以毫秒为单位的超时延迟,作为参数。

Hope, it will help you!希望,它会帮助你!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM