承诺链中的承诺之间存在延迟

16

假设我正在使用以下代码按顺序运行一些Promise:

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

这段代码简单地调用了我的特殊函数(该函数返回一个Promise),等待Promise被解决,然后再次调用我的特殊函数等等。所以对于数组中的每个元素,函数都会被调用一次,并按正确的顺序执行。

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

重要的是Promises按照正确的顺序执行,而且mySpecialFunction的执行时间每次都有所不同。

我想同步休眠可能有效,但我不打算在单独的线程中运行此代码,因此它会导致浏览器中烦人的UI冻结。

我不确定setTimer是否可以用于此。我的意思是,我不能延迟Promise的返回。


1
这可能会有所帮助 http://bluebirdjs.com/docs/api/promise.delay.html - Simon H
1
我已经看过了,但我没有使用bluebird。我正在使用原生的Promises。(ECMA6) - Forivin
7个回答

25
答案如下:

答案很好,但是它们等待的时间太长了,因为所有答案都会等待,而不管实际操作是否已经超过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());

2
打败我37秒 :-) - Bergi

5

有一个非常方便的实用函数,我称之为delay()

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

然后,您可以像这样在一个Promise链中使用它:
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
});

4
要延迟至少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());

3
以下是一个示例,演示如何实现一个不会阻塞但会等待指定时间段的承诺(Promise):
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())

2

请看这里: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()的结果。只有这样才能链接promise。

请注意,我将延迟更改为1000毫秒,仅供强调。


0

这是我关于延迟 Promise 序列的完整解决方案:


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

    });


  }

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

希望能对你有所帮助!


0

由于这似乎是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毫秒的延迟。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接