承诺链中承诺之间的延迟

Posted

技术标签:

【中文标题】承诺链中承诺之间的延迟【英文标题】:Delays between promises in promise chain 【发布时间】:2017-04-26 01:16:39 【问题描述】:

假设我正在使用以下代码连续运行几个 Promise:

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

代码只是简单地调用 mySpecialFunction(它返回一个 Promise),等待 Promise 被解析,然后再次调用 mySpecialFunction 等等。因此,该函数以正确的顺序为数组中的每个元素调用一次。

如何确保每次调用 mySpecialFunction(item) 之间至少有 50 毫秒的延迟?

以正确的顺序执行 promise 很重要,mySpecialFunction 的执行时间每次都不同。

我猜同步睡眠会起作用,但我不打算在单独的线程中运行这段代码,所以它会导致浏览器中烦人的 ui 冻结。

我不确定 setTimer 是否可以用于此目的。我的意思是我不能延迟承诺的回报。

【问题讨论】:

这可能对bluebirdjs.com/docs/api/promise.delay.html有帮助 我已经看到了,但我没有使用蓝鸟。我正在使用本机承诺。 (ECMA6) 【参考方案1】:

答案很好,但它们等待的时间太长,因为所有答案都等待实际操作是否已经花费了超过 50 毫秒。

您可以使用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());

【讨论】:

比我快 37 秒 :-)【参考方案2】:

我称之为delay()

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

然后,您可以像这样在承诺链中使用它:

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());

您还可以创建一个小实用函数来执行带有可选延迟的顺序迭代:

// 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());

然后,你会像这样使用它:

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

【讨论】:

【参考方案3】:

要获得至少 50ms 的延迟,请使用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());

【讨论】:

【参考方案4】:

以下示例说明了如何实现不阻塞但等待指定时间段的承诺:

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);
    );

因此,根据您的具体需求,您应该能够执行以下操作:

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())

【讨论】:

【参考方案5】:

给你: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 必须是 p.then() 的结果。只有这样你才能链接承诺。

注意,我将其更改为 1000 毫秒延迟只是为了强调。

【讨论】:

【参考方案6】:

因为这似乎是mySpecialFunction 的要求,所以我会在那里实现它。这样如果函数在最后一次调用后不到 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();
        )
    

然后:

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())

所以无论在哪里/如何调用mySpecialFunction 都无关紧要,在它运行传递的回调中的代码之前总会有至少 50 毫秒的延迟。

【讨论】:

【参考方案7】:

这是我对延迟承诺序列的完整解决方案:


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);

    );


  

它接收承诺数组和它们之间的超时延迟(毫秒)作为参数。

希望对你有帮助!

【讨论】:

以上是关于承诺链中承诺之间的延迟的主要内容,如果未能解决你的问题,请参考以下文章

为啥延迟处理承诺拒绝有时会导致 UnhandledPromiseRejectionWarning?

异步 JavaScript - 回调与延迟/承诺 [重复]

那么如何在承诺中添加延迟[重复]

javascript 延迟承诺

向返回承诺数组的 Array.map 调用添加毫秒延迟

AWS RDS PostgreSQL:PostgreSQL 复制延迟的承诺价值是多少?