向Array.map调用添加毫秒级延迟,该延迟将返回promise数组 [英] Add milliseconds delay to Array.map calls which returns Array of promises

查看:300
本文介绍了向Array.map调用添加毫秒级延迟,该延迟将返回promise数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的需求很简单.我想将对sendEmail的调用延迟100毫秒.电子邮件服务提供商最多允许每秒发送10封电子邮件.

My need is simple. I would like to delay calls to sendEmail by 100 milliseconds. The email service provider permits at most sending 10 emails per second.

请注意,尽管.map是同步的,但它立即返回Promise.

Note, however, though .map is synchronous, it immediately returns a Promise.

我尝试setTimeout都无济于事,例如setTimeout(() => resolve(x), 100)setTimeout(() => {return new Promise....}, 100).

I have tried setTimeout to no avail, such as setTimeout(() => resolve(x), 100) and setTimeout(() => {return new Promise....}, 100).

有想法吗?

const promises = userEmailArray.map((userEmail) => {
  return new Promise((resolve, reject) => {
      ....
      mailer.sendEmail(userEmail);
      return resolve();
    });
  });
});
...
Promise.all(promises).then(() => resolve()).catch(error => reject(error));

推荐答案

有很多方法可以解决此问题.我可能自己会使用一个递归链式promise,然后可以根据上一次调用的结束时间更精确地使用一个计时器,并且可以使用promise来调用它并处理错误的传播.

There are a bunch of different ways to approach this. I'd probably just use a recursive chained promise myself and then you can more precisely use a timer based on the finish from the previous call and you can use promises for calling it and handling propagation of errors.

我在这里假设您的mailer.sendEmail()遵循node.js回调调用约定,因此我们需要对其进行承诺化".如果它已经返回了承诺,那么您可以直接使用它代替我创建的sendEmail()函数.

I've assumed here that your mailer.sendEmail() follows the node.js callback calling convention so we need to "promisify" it. If it already returns a promise, then you can use it directly instead of the sendEmail() function that I created.

无论如何,这里有很多不同的方法.

Anyways, here are a bunch of different approaches.

延迟后调用相同功能(递归延迟)

// make promisified version - assumes it follows node.js async calling convention
let sendEmail = util.promisify(mailer.sendEmail);

function delay(t, data) {
    return new Promise(resolve => {
        setTimeout(resolve.bind(null, data), t);
    });
}

function sendAll(array) {
    let index = 0;
    function next() {
        if (index < array.length) {
            return sendEmail(array[index++]).then(function() {
                return delay(100).then(next);
            });
        }        
    }
    return Promise.resolve().then(next);
}

// usage
sendAll(userEmailArray).then(() => {
    // all done here
}).catch(err => {
    // process error here
});


使用setInterval来控制步速

您也可以只使用setInterval来每隔100ms发出一个新请求,直到数组为空:

You could also just use a setInterval to just launch a new request every 100ms until the array was empty:

// promisify
let sendEmail = util.promisify(mailer.sendEmail);

function sendAll(array) {
    return new Promise((resolve, reject) => {
        let index = 0;
        let timer = setInterval(function() {
            if (index < array.length) {
                sendEmail(array[index++]).catch(() => {
                    clearInterval(timer);
                    reject();                        
                });
            } else {
                clearInterval(timer);
                resolve();
            }
        }, 100);
    })
}


使用等待暂停循环

而且,您可以在ES6中使用await来暂停"循环:

And, you could use await in ES6 to "pause" the loop:

// make promisified version - assumes it follows node.js async calling convention
let sendEmail = util.promisify(mailer.sendEmail);

function delay(t, data) {
    return new Promise(resolve => {
        setTimeout(resolve.bind(null, data), t);
    });
}

// assume this is inside an async function    
for (let userEmail of userEmailArray) {
    await sendEmail(userEmail).then(delay.bind(null, 100));
}


.reduce()与承诺一起用于对阵列的访问顺序


Use .reduce() with Promises to Sequence Access to Array

如果您不想累积结果数组,而只是想排序,那么一种典型的方法是使用由.reduce()驱动的承诺链:

If you aren't trying to accumulate an array of results, but just want to sequence, then a canonical ways to do that is using a promise chain driven by .reduce():

// make promisified version - assumes it follows node.js async calling convention
let sendEmail = util.promisify(mailer.sendEmail);

function delay(t, data) {
    return new Promise(resolve => {
        setTimeout(resolve.bind(null, data), t);
    });
}

userEmailArray.reduce(function(p, userEmail) {
    return p.then(() => {
        return sendEmail(userEmail).then(delay.bind(null, 100));
    });
}, Promise.resolve()).then(() => {
    // all done here
}).catch(err => {
    // process error here
});


同时使用Bluebird功能进行并发控制和延迟

蓝鸟诺言库内置了几个有用的功能,可在此处提供帮助:

The Bluebird promise library has a couple useful features built in that help here:

const Promise = require('Bluebird');
// make promisified version - assumes it follows node.js async calling convention
let sendEmail = Promise.promisify(mailer.sendEmail);

Promise.map(userEmailArray, userEmail => {
    return sendEmail(userEmail).delay(100);
}, {concurrency: 1}).then(() => {
    // all done here
}).catch(err => {
    // process error here
});

请注意,同时使用{concurrency: 1}功能来控制正在处理的请求数和内置的.delay(100)许诺方法.

Note the use of both the {concurrency: 1} feature to control how many requests are in-flight at the same time and the built-in .delay(100) promise method.

创建通常可以使用的序列助手

而且,创建一个小的辅助函数来对数组进行排序(在两次迭代之间有延迟)可能会很有用:

And, it might be useful to just create a little helper function for sequencing an array with a delay between iterations:

function delay(t, data) {
    return new Promise(resolve => {
        setTimeout(resolve.bind(null, data), t);
    });
}

async function runSequence(array, delayT, fn) {
    for (item of array) {
        await fn(item).then(data => {
            return delay(delayT, data);
        });
    }
}

然后,您可以在需要时使用该助手:

Then, you can just use that helper whenever you need it:

// make promisified version - assumes it follows node.js async calling convention
let sendEmail = util.promisify(mailer.sendEmail);

runSequence(userEmailArray, sendEmail, 100).then(() => {
    // all done here
}).catch(err => {
    // process error here
});

这篇关于向Array.map调用添加毫秒级延迟,该延迟将返回promise数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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