I'm testing a library I wrote to throttle execution of a function.
The API is throttler.do(fn) and it returns a promise of fn's return value (resolved at whichever point the throttler decides it's ok to run it).
I'm using lolex to fake Date() and setTimeout so if I set the throttler to allow two actions per minute and do
throttler.do(() => {});
throttler.do(() => {});
throttler.do(() => {}).should.eventually.equal(5);
this fails as expected (it times out because it's waiting on the last promise forever since I never call lolex.tick).
Is there a way I can turn this into a passing test, something like
throttler.do(() => {}).should.never.be.fulfilled;
I can't do
setTimeout(() => done(), 1500)
because the setTimeout method is faked by lolex.
You can mock the Promise to be synchronous and then check the Promise state. Here is a library you can use promise-sync
Related
I have the following code:
Promise
.resolve('333')
.then(()=>{setTimeout(()=>{Promise.resolve('123');},10000)})
.then(()=>{console.log("should wait");});
I thought that the output should be first '123' and then 'should wait'. From unclear reason the 'should wait' is printed first. I thought that the second then won't start until the asynchrony function (setTimeout) won't finished. I read that this is all the "magic" of using Promise and then. Now i'm very confused. Why for example it doesn't happen when we call fetch function? fetch function is also asynchrony, so why the then after the fetch doesn't start before the fetch ends?
Unless a .then callback explicitly returns a Promise, the next .then in the chain is guaranteed to run nearly instantly afterwards (it gets put into the microtask queue).
Right now, you're not returning anything, so undefined gets returned, so the second .then runs immediately.
If you want the first .then to cause the second to wait until the timeout finishes, return a Promise that resolves when the timeout resolves:
Promise.resolve('333')
.then(() => {
return new Promise((res) => {
setTimeout(() => {
res('123');
}, 3000);
});
})
.then(() => { console.log("should wait 3 seconds"); });
I am having trouble finding a use for Promises. Wouldn't these 2 approaches below work the same exact way? Since the while loop in loopTest() is synchronous, logStatement() function wouldn't run until it's complete anyways so how would the the 2nd approach be any different ..wouldn't it be pointless in waiting for it to resolve() ?
1st approach:
function loopTest() {
while ( i < 10000 ) {
console.log(i)
i++
})
}
function logStatement() {
console.log("Logging test")
}
loopTest();
logStatement();
2nd approach:
function loopTest() {
return new Promise((resolve, reject) => {
while ( i < 10000 ) {
console.log(i)
i++
if (i === 999) {
resolve('I AM DONE')
}
})
});
}
function logStatement() {
console.log("Logging test")
}
loopTest().then(logStatement());
Promises don't make anything asynchronous,¹ so you're right, there's no point to using a promise in the code you've shown.
The purpose of promises is to provide a standard, composable means of observing the result of things that are already asynchronous (like ajax calls).
There are at least three massive benefits to having a standardized way to observe the results of asynchronous operations:
We can have standard semantics for consuming individual promises, rather than every API defining its own signature for callback functions. (Does it signal error with an initial parameter that's null on success, like Node.js? Does it call the callback with an object with a success flag? Or...)
We can have standard ways of composing/combining them, such as Promise.all, Promise.race, Promise.allSettled, etc.
We can have syntax to consume them with our usual control structures, which we have now in the form of async functions and await.
But again, throwing a promise at a synchronous process almost never does anything useful.²
¹ One very small caveat there: The handler functions to attach to a promise are always triggered asynchronously, whether the promise is already settled or not.
² Another small caveat: Sometimes, you have a synchronous result you want to include in a composition operation (Promise.all, etc.) with various asynchronous operations. In that case, wrapping the value in a promise that's instantly fulfilled is useful — and in fact, all the standard promise combinators (Promise.all, etc.) do that for you, as does await.
There's no point in what you are doing, because your function body is just a blocking loop.
To get a benefit from Promises, use it with APIs that do something with IO, such as a HTTP request, or reading a file from disk.
These APIs all traditionally used callbacks, and are now mostly Promise based.
Anything function that uses a Promise-based function, should itself also be Promise-based. This is why you see a lot of promises in modern code, as a promise only has to be used at 1 level in a stack for the entire stack to be asynchronous in nature.
Is this a better example of how Promises are used? This is all I can think of to make it show use to me:
Version 1
function getData() {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(data => data.json())
.then(json => console.log(json))
}
function logInfo() {
console.log("i am a logger")
}
getData()
logInfo()
// "I am a logger"
// {"test": "json"}
Version 2
function getData() {
return fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(data => data.json())
.then(json => console.log(json))
}
function logInfo() {
console.log("i am a logger")
}
getData().then(logInfo);
// "{"test": "json"}
// "I am a logger"
// waits for API result to log _then_ logInfo is run , which makes a log statement
There's definitely benefits to using Promises but that's only in certain scenarios where their usage would seem viable.
Your example could represent what would happen when you retrieve data from an external source synchronously, it would block the thread preventing further code from executing until the loop terminates (I explain below why exactly that happens) - wrapping it in a promise gives no different output in that the thread is still being blocked and when the next message in the queue has to be processed, it gets processed like normal right after it ends.
However an implementation similar to this could achieve a while loop running in a non-blocking manner, just an idea (don't mean to derail this topic with setInterval's implementation):
let f = () => {
let tick = Date.now;
let t = tick();
let interval = setInterval(() => {
if (tick() - t >= 3000) {
console.log("stop");
clearInterval(interval);
}
}, 0);
};
f()
console.log("start");
Basically the time is checked/handled in a separate thread in the browser and the callback is executed every time the time specified runs out while the interval hasn't been cleared, after the call stack becomes empty (so UI function isn't affected) and the current executing function terminates/ends or after other functions above it in the stack finish running. I don't know about the performance implications of doing something like this but I feel like this should only be used when necessary, since the callback would have to be executed very frequently (with 0 timeout, although it's not guaranteed to be 0 anyway).
why it happens
I mainly want to clarify that while the handler functions will be scheduled to be executed asynchronously, every message in the queue has to be processed completely before the next one and for the duration your while loop executes, no new message can be processed in the event queue so it would be pointless to involve Promises where the same thing would happen without them.
So basically the answer to:
wouldn't it be pointless in waiting for it to resolve() ?
is yes, it would be pointless in this case.
I have an Ember Promise returned as the result of a service method I am testing. I'm using Ember 2.16 and qunit.
let promise = service.myFunctionBeingTested();
promise.then((resolveParam) => {
assert.equal(resolveParam, EXPECTED_VAL);
}).catch(() => {
assert.notOk("Promise rejected");
}
// Code here is supposed to trigger resolution of promise...
// Code here to make sure the promise is resolved and assert is run
What can I use (after the promise should be resolved) to make sure the promise is resolved and assert.equal(resolveParam, EXPECTED_VAL); is run?
It seems like it should be easy to check that the promise is settled, but Ember's documentation on rsvp promises is either too difficult to navigate or absent 😊 I could use assert.expect(/* COUNT */) but then future users will have to update this number every time new assertions are added which I want to avoid.
At the beginning of your test, use assert.expect(1) to tell the test you are expecting exactly 1 assert. Then you should be good. If the promise fails, your notOk will be called and the test will fail. If the promise resolves, the test will pass.
If this is not an acceptance test, you may need to:
import wait from 'ember-test-helpers/wait'
then return the wait call from your test to make sure the test waits for your promise before checking if it got the right number of assertions:
return wait()
UPDATE
The other way you can do this without assert.expect is to declare a variable before like:
let successCalled = false
Then in the success block
promise.then((resolveParam) => {
successCalled = true
})
Then after, do:
return wait().then(() => {
assert.ok(successCalled)
})
Although, assert.expect exists for this purpose so I recommend using it.
I'm trying to understand Jest's asynchronous testing.
My module has a function which accepts a boolean and returns a Promise of a value. The executer function calls setTimeout, and in the timed out callback the promise resolves or rejects depending on the boolean initially supplied. The code looks like this:
const withPromises = (passes) => new Promise((resolve, reject) => {
const act = () => {
console.log(`in the timout callback, passed ${passes}`)
if(passes) resolve('something')
else reject(new Error('nothing'))
}
console.log('in the promise definition')
setTimeout(act, 50)
})
export default { withPromises }
I'd like to test this using Jest. I guess that I need to use the mock timers Jest provides, so my test script looks a bit like this:
import { withPromises } from './request_something'
jest.useFakeTimers()
describe('using a promise and mock timers', () => {
afterAll(() => {
jest.runAllTimers()
})
test('gets a value, if conditions favor', () => {
expect.assertions(1)
return withPromises(true)
.then(resolved => {
expect(resolved).toBe('something')
})
})
})
I get the following error/failed test, whether or not I call jest.runAllTimers()
Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Can you explain where I'm going wrong and what I might do to get a passing test that the promise resolves as expected?
The call to jest.useFakeTimers() mocks every timer function with one that you must control. Instead of the timer running automatically, you would advance it manually. The jest.runTimersToTime(msToRun) function would advance it by msToRun milliseconds. It's very common that you want to fast-forward until every timer has elapsed and it would be cumbersome to calculate the time it takes for all the timers to finish, so Jest provides jest.runAllTimers(), which pretends that enough time has passed.
The problem in your test is that you never call jest.runAllTimers() in the test, but you call it in the afterAll hook, which is called after the tests have finished. During your test the timer remains at zero so your callback is never actually called and Jest aborts it after a predefined interval (default: 5s) to prevent being stuck with a potentially endless test. Only after the test has timed out, you call jest.runAllTimers(), at which point it doesn't do anything, since all tests have already finished.
What you need to do is launch the promise and then advance the timer.
describe('using a promise and mock timers', () => {
test('gets a value, if conditions favor', () => {
expect.assertions(1)
// Keep a reference to the pending promise.
const pendingPromise = withPromises(true)
.then(resolved => {
expect(resolved).toBe('something')
})
// Activate the timer (pretend the specified time has elapsed).
jest.runAllTimers()
// Return the promise, so Jest waits for its completion and fails the
// test when the promise is rejected.
return pendingPromise
})
})
In the OpenUI5 code-base I came across this snippet:
// Wait until everything is rendered (parent height!) before reading/updating sizes.
// Use a promise to make sure
// to be executed before timeouts may be executed.
Promise.resolve().then(this._updateTableSizes.bind(this, true));
It looks like the native Promise function is being used, with no argument being passed to it's resolve function which takes an:
Argument to be resolved by this Promise. Can also be a Promise or a
thenable to resolve.
So, since it looks like the promise would simply immediately resolve and invoke then's callback, perhaps the intent is similar to:
var self = this;
setTimeout(function() {
self._updateTableSizes.bind(self, true)
}, 0);
...basically, freeing the JavaScript run-time event-loop to finish other things (like rendering) and then come right back to the callback?
My question is:
Is this a common pattern? Best-practice? Are there any advantages/disadvantages to either approach?
Yes, Promise.resolve() does immediately fulfill with the undefined value that you implicitly passed in. The callback is still executed asynchronously - quite like in the setTimeout snippet you posted.
However, as the comment in the code explains, the intent is not just to execute the callback asynchronously:
Use a promise to make sure to be executed before timeouts may be executed.
Promise callbacks do run before timeouts or other events, and these subtle timing differences are sometimes important. Given that choice of the task loop is usually not important, No this is not a common pattern; but it is a valid pattern that does exactly what you need when you need it.
I noticed the technique in this polyfill: https://github.com/wicg/inert (with comment)
const newButton = document.createElement('button');
const inertContainer = document.querySelector('[inert]');
inertContainer.appendChild(newButton);
// Wait for the next microtask to allow mutation observers to react to the DOM change
Promise.resolve().then(() => {
expect(isUnfocusable(newButton)).to.equal(true);
});