有很多不同的方法可以解决这个问题。我自己可能只使用递归链式 Promise,然后您可以更精确地使用基于上一次调用的完成的计时器,您可以使用 Promise 来调用它并处理错误的传播。
我在这里假设您的 mailer.sendEmail() 遵循 node.js 回调调用约定,因此我们需要“承诺”它。如果它已经返回了一个 promise,那么你可以直接使用它来代替我创建的 sendEmail() 函数。
无论如何,这里有很多不同的方法。
延迟后调用相同的函数(延迟递归)
// 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 来每隔 100 毫秒发起一个新请求,直到数组为空:
// 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);
})
}
使用 await 暂停循环
而且,您可以在 ES6 中使用 await 来“暂停”循环:
// 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() 与 Promises 一起使用以对数组的访问进行排序
如果您不尝试累积结果数组,而只是想排序,那么一种规范的方法是使用由.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 功能用于并发控制和延迟
Bluebird promise library 内置了一些有用的功能,可在此处提供帮助:
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) promise 方法。
创建通用的序列助手
而且,创建一个小辅助函数来对具有迭代之间延迟的数组进行排序可能会很有用:
function delay(t, data) {
return new Promise(resolve => {
setTimeout(resolve, t, data);
});
}
async function runSequence(array, delayT, fn) {
let results = [];
for (let item of array) {
let data = await fn(item);
results.push(data);
await delay(delayT);
}
return results;
}
然后,您可以在需要时使用该助手:
// make promisified version - assumes it follows node.js async calling convention
let sendEmail = util.promisify(mailer.sendEmail);
runSequence(userEmailArray, 100, sendEmail).then(() => {
// all done here
}).catch(err => {
// process error here
});