Tho*_*ggi 6 javascript promise bluebird
我正在尝试为promiseRateLimit下面的函数创建一个有效的测试用例.该promiseRateLimit函数的工作方式是使用a queue来存储传入的promises并将delay它们放在它们之间.
import Promise from 'bluebird'
export default function promiseRateLimit (fn, delay, count) {
let working = 0
let queue = []
function work () {
if ((queue.length === 0) || (working === count)) return
working++
Promise.delay(delay).tap(() => working--).then(work)
let {self, args, resolve} = queue.shift()
resolve(fn.apply(self, args))
}
return function debounced (...args) {
return new Promise(resolve => {
queue.push({self: this, args, resolve})
if (working < count) work()
})
}
}
Run Code Online (Sandbox Code Playgroud)
以下是该功能的实例.
async function main () {
const example = (v) => Promise.delay(50)
const exampleLimited = promiseRateLimit(example, 100, 1)
const alpha = await exampleLimited('alpha')
const beta = await exampleLimited('beta')
const gamma = await exampleLimited('gamma')
const epsilon = await exampleLimited('epsilon')
const phi = await exampleLimited('phi')
}
Run Code Online (Sandbox Code Playgroud)
该example承诺需要50ms运行和promiseRateLimit功能将只允许1答应每100ms.所以承诺之间的间隔应该大于100ms.
这是一个完整的测试,有时会返回成功,有时会失败:
import test from 'ava'
import Debug from 'debug'
import Promise from 'bluebird'
import promiseRateLimit from './index'
import {getIntervalsBetweenDates} from '../utilitiesForDates'
import {arraySum} from '../utilitiesForArrays'
import {filter} from 'lodash'
test('using async await', async (t) => {
let timeLog = []
let runCount = 0
const example = (v) => Promise.delay(50)
.then(() => timeLog.push(new Date))
.then(() => runCount++)
.then(() => v)
const exampleLimited = promiseRateLimit(example, 100, 1, 'a')
const alpha = await exampleLimited('alpha')
const beta = await exampleLimited('beta')
const gamma = await exampleLimited('gamma')
const epsilon = await exampleLimited('epsilon')
const phi = await exampleLimited('phi')
const intervals = getIntervalsBetweenDates(timeLog)
const invalidIntervals = filter(intervals, (interval) => interval < 100)
const totalTime = arraySum(intervals)
t.is(intervals.length, 4)
t.deepEqual(invalidIntervals, [])
t.deepEqual(totalTime >= 400, true)
t.is(alpha, 'alpha')
t.is(beta, 'beta')
t.is(gamma, 'gamma')
t.is(epsilon, 'epsilon')
t.is(phi, 'phi')
})
Run Code Online (Sandbox Code Playgroud)
我创建了一个getIntervalsBetweenDates函数,它简单地区分了两个unix时间戳,并获得了一组日期之间的持续时间.
export function getIntervalsBetweenDates (dates) {
let intervals = []
dates.forEach((date, index) => {
let nextDate = dates[index + 1]
if (nextDate) intervals.push(nextDate - date)
})
return intervals
}
Run Code Online (Sandbox Code Playgroud)
问题是上面的测试有时会返回一个低于的测试间隔delay.例如如果delay是100ms有时的间隔返回98ms或96ms.没有理由这应该发生.
有没有办法让上述测试100%的时间通过?我正在努力确保delay论证有效,并且承诺之间至少有那么多时间.
更新2016-12-28 9:20 am(EST)
这是完整的测试
import test from 'ava'
import Debug from 'debug'
import Promise from 'bluebird'
import promiseRateLimit from './index'
import {getIntervalsBetweenDates} from '../utilitiesForDates'
import {arraySum} from '../utilitiesForArrays'
import {filter} from 'lodash'
test('using async await', async (t) => {
let timeLog = []
let runCount = 0
let bufferInterval = 100
let promisesLength = 4
const example = v => {
timeLog.push(new Date)
runCount++
return Promise.delay(50, v)
}
const exampleLimited = promiseRateLimit(example, bufferInterval, 1)
const alpha = await exampleLimited('alpha')
const beta = await exampleLimited('beta')
const gamma = await exampleLimited('gamma')
const epsilon = await exampleLimited('epsilon')
const phi = await exampleLimited('phi')
const intervals = getIntervalsBetweenDates(timeLog)
const invalidIntervals = filter(intervals, (interval) => interval < bufferInterval)
const totalTime = arraySum(intervals)
t.is(intervals.length, promisesLength)
t.deepEqual(invalidIntervals, [])
t.deepEqual(totalTime >= bufferInterval * promisesLength, true)
t.is(alpha, 'alpha')
t.is(beta, 'beta')
t.is(gamma, 'gamma')
t.is(epsilon, 'epsilon')
t.is(phi, 'phi')
})
test('using Promise.all with 2 promises', async (t) => {
let timeLog = []
let runCount = 0
let bufferInterval = 100
let promisesLength = 1
const example = v => {
timeLog.push(new Date)
runCount++
return Promise.delay(50, v)
}
const exampleLimited = promiseRateLimit(example, bufferInterval, 1)
const results = await Promise.all([exampleLimited('alpha'), exampleLimited('beta')])
const intervals = getIntervalsBetweenDates(timeLog)
const invalidIntervals = filter(intervals, (interval) => interval < bufferInterval)
const totalTime = arraySum(intervals)
t.is(intervals.length, promisesLength)
t.deepEqual(invalidIntervals, [])
t.deepEqual(totalTime >= bufferInterval * promisesLength, true)
})
test('using Promise.props with 4 promises', async (t) => {
let timeLog = []
let runCount = 0
let bufferInterval = 100
let promisesLength = 3
const example = v => {
timeLog.push(new Date)
runCount++
return Promise.delay(200, v)
}
const exampleLimited = promiseRateLimit(example, bufferInterval, 1)
const results = await Promise.props({
'alpha': exampleLimited('alpha'),
'beta': exampleLimited('beta'),
'gamma': exampleLimited('gamma'),
'delta': exampleLimited('delta')
})
const intervals = getIntervalsBetweenDates(timeLog)
const invalidIntervals = filter(intervals, (interval) => interval < bufferInterval)
const totalTime = arraySum(intervals)
t.is(intervals.length, promisesLength)
t.deepEqual(invalidIntervals, [])
t.deepEqual(totalTime >= bufferInterval * promisesLength, true)
t.is(results.alpha, 'alpha')
t.is(results.beta, 'beta')
t.is(results.gamma, 'gamma')
t.is(results.delta, 'delta')
})
test('using Promise.props with 12 promises', async (t) => {
let timeLog = []
let runCount = 0
let bufferInterval = 100
let promisesLength = 11
const example = v => {
timeLog.push(new Date)
runCount++
return Promise.delay(200, v)
}
const exampleLimited = promiseRateLimit(example, bufferInterval, 1)
const results = await Promise.props({
'a': exampleLimited('a'),
'b': exampleLimited('b'),
'c': exampleLimited('c'),
'd': exampleLimited('d'),
'e': exampleLimited('e'),
'f': exampleLimited('f'),
'g': exampleLimited('g'),
'h': exampleLimited('h'),
'i': exampleLimited('i'),
'j': exampleLimited('j'),
'k': exampleLimited('k'),
'l': exampleLimited('l')
})
const intervals = getIntervalsBetweenDates(timeLog)
console.log(intervals)
const invalidIntervals = filter(intervals, (interval) => interval < bufferInterval)
const totalTime = arraySum(intervals)
t.is(intervals.length, promisesLength)
t.deepEqual(invalidIntervals, [])
t.deepEqual(totalTime >= bufferInterval * promisesLength, true)
})
Run Code Online (Sandbox Code Playgroud)
即使有了example改变,我仍然会遇到这个问题.
[ 99, 98, 105, 106, 119, 106, 105, 105, 101, 106, 100 ]
2 passed
2 failed
using Promise.props with 4 promises
t.deepEqual(invalidIntervals, [])
|
[99]
Generator.next (<anonymous>)
using Promise.props with 12 promises
t.deepEqual(invalidIntervals, [])
|
[99,98]
Generator.next (<anonymous>)
Run Code Online (Sandbox Code Playgroud)
setTimeout(内部使用Promise.delay)不保证准确的计时,它只确保回调函数不会在给定的超时到期之前被调用。实际时间将取决于机器负载、事件循环的速度以及可能的其他因素。
事实上,Node.js 文档仅指出
该命令
callback可能不会在精确的delay毫秒内被调用。Node.js 不保证回调触发的确切时间,也不保证回调的顺序。回调将尽可能接近指定的时间被调用。
在您的测试中会发生的情况是,Promise.delay(50)有时需要超过 50 毫秒(虽然不是很多,但仍然如此),并且当下一个日志Promise.delay(50)更准时时,与以下日志的差异可能会变得小于 100 毫秒。
如果您只是立即记录函数的调用时间,而不是在大约example50 毫秒的人为延迟之后,您应该能够减轻这种影响:
const example = v => {
timeLog.push(new Date);
runCount++;
return Promise.delay(50, v)
};
Run Code Online (Sandbox Code Playgroud)
为了解决 100 毫秒超时本身的不准确性,最简单的解决方案是给它一些可能 5% 的余地(在您的情况下为 5 毫秒):
const invalidIntervals = filter(intervals, (interval) => interval < 100 * .95)
t.true(totalTime >= 400 * .95)
Run Code Online (Sandbox Code Playgroud)
如果您想绝对确保延迟不会太短,您可以编写自己的函数:
Promise.delayAtLeast = function(delay, value) {
const begin = Date.now()
return Promise.delay(delay, value).then(function checkTime(v) {
const duration = Date.now() - begin;
return duration < delay
? Promise.delay(delay - duration, v).then(checkTime);
: v;
});
};
Run Code Online (Sandbox Code Playgroud)
并在 中使用它promiseRateLimit。
| 归档时间: |
|
| 查看次数: |
119 次 |
| 最近记录: |