Test that a function awaits something before doing anything else - javascript

Suppose I have a function that executes an asynchronous action (doStuffAsync()) and then intends to do some other stuff (doOtherStuff()).
doStuffAsync() returns a Promise.
Also assume everything is mockable.
How do I test that my function awaits doStuffAsync() before trying to doOtherStuff()?
I thought of mocking doStuffAsync() using resolve => setTimeout(resolve(), timeout), but timeout-based testing looks very fragile.

You need a flag accessible by both doStuffAsync and doOtherStuff.
In doStuffAsync() write in that flag
In doOtherStuff() read from that flag and determine if it was written
Something like:
var isAsyncRunning = false;
function doStuffAsync(){
isAsyncRunning = true;
new Promise(function(resolve, reject) {
setTimeout(()=>{
isAsyncRunning = false;
resolve(); //irrelevant in this exercise
}, 1000);
});
}
doStuffAsync();
function doOtherStuff(){
if(isAsyncRunning){
console.log("Async is running.");
} else {
console.log("Async is no longer running.");
};
}
doOtherStuff();
setTimeout(() => {
//calling doOtherStuff 2 seconds later..
doOtherStuff();
}, 2000);

I managed to complete it with a less ugly solution than setTimeout – setImmediate.
function testedFunction() {
await MyModule.doStuffAsync();
MyModule.doOtherStuff();
}
it('awaits the asynchronous stuff before doing anything else', () => {
// Mock doStuffAsync() so that the promise is resolved at the end
// of the event loop – which means, after the test.
// -
const doStuffAsyncMock = jest.fn();
const delayedPromise = new Promise<void>(resolve => setImmediate(resolve()));
doStuffAsyncMock.mockImplementation(() => delayedPromise);
const doOtherStuffMock = jest.fn();
MyModule.doStuffAsync = doStuffAsyncMock;
MyModule.doOtherStuffMock = doOtherStuffMock;
testedFunction();
expect(doOtherStuffMock).toHaveBeenCalledTimes(0);
}
setImmediate will put off the resolution of your promise to the end of the event loop, which is after your test completes.
So, your assert that doOtherStuff() was not invoked:
Will pass if there is an await inside the testedFunction
Will fail if there isn't.

Related

"A little help!" with the asynchronous syntax in javascript. I'm an old newbie, and I've finding this more than a little frustrating

I’d just like to run an async function, have it wait till it has a return value, pass that return value to another async function and wash and repeat a few times. I’ve seen some examples but I’m struggling to make them work in test code. Forgive all the baseball context, but I had to have a little fun with this self-inflicted nose bleed.
I can't even make it work without passing return values in the attached code, feeling pretty stupid at the moment...
whosOnFirst(1)
.then(whatsOnSecond(2))
.then(iDunnosOnthird(3))
I'd like to see the syntax to have whosOnFirst(1) execute, pass a return value to whatsOnSecond() and pass it's return value to iDunnosOnthird(). Creating the desired output (less the spaces) referenced in the bottom of the snippet.
I really appreciate the help!
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
return pause + 1; // delayed return value / adds 1 to input param
}, pause * 1000);
}
// Who's On First - run this first with an input param = 1
async function whosOnFirst(i) {
waitSec("who's on first", 1).then(result => {
return result;
})
}
// What's on Second - run after completion of whosOnFirst() using return for param
async function whatsOnSecond(i) {
waitSec("what's on second", i).then(result => {
return result;
})
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
async function iDunnosOnthird(i) {
waitSec("iDunno's on third", i).then(result => {
return result;
})
}
whosOnFirst(1)
.then(whatsOnSecond(2))
.then(iDunnosOnthird(3))
// CURRENT OUTPUT:
// who's on first called, waiting 1 second(s)...
// what's on second called, waiting 2 second(s)...
// iDunno's on third called, waiting 3 second(s)...
// who's on first sent 2 in a return, and done!
// what's on second sent 3 in a return, and done!
// iDunno's on third sent 4 in a return, and done!
// DESIRED OUTPUT:
// who's on first called, waiting 1 second(s)...
// who's on first sent 2 in a return, and done!
// what's on second called, waiting 2 second(s)...
// what's on second sent 3 in a return, and done!
// iDunno's on third called, waiting 3 second(s)...
// iDunno's on third sent 4 in a return, and done!
Working Solution
Your issue is that the waitSec is in a different context from the rest of the code. You need to lift it to the Promise context.
Then you have "promisified" code all the way down, and you can chain promises. Here it is working (further explanation below it):
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
return new Promise(resolve => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
function whosOnFirst(i) {
return waitSec("who's on first", 1)
}
// What's on Second - run after completion of whosOnFirst() using return for param
function whatsOnSecond(i) {
return waitSec("what's on second", i)
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
function iDunnosOnthird(i) {
return waitSec("iDunno's on third", i)
}
whosOnFirst(1)
.then(whatsOnSecond)
.then(iDunnosOnthird)
.then(console.log)
Further Explanation
Your original implementation of waitSec always returns undefined to the caller:
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
return pause + 1; // delayed return value / adds 1 to input param
}, pause * 1000);
// return undefined happens here
}
The return inside the setTimeout does not return to your calling code, because your code never calls this function. It is invoked by the JS timer code, asynchronously.
Returning values from async code using a callback
The way to communicate a result out of there to a caller is to either take a callback as a parameter to the outer function and call that callback in the async function, like this:
function waitSec({callerName, pause, callback}) {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
callback(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
}
In which case you have the classic callback pattern of JS.
Using Promises, and why you might prefer them
Or, you return a Promise, and resolve it to return the value - as I demonstrated in the solution. For more about that, search for "how do I promisify a callback".
The benefit of doing it this way (with a Promise) is that Promises are composable, as demonstrated in the solution.
Promises are also await-able, so you can use async/await with Promise-returning functions.
You do not need to mark the Promise-returning functions as async.
If you mark a function as async, it returns a Promise, like this:
// A function marked async returns a Promise
async function simple() {
return 3
}
// Proof that it is a Promise
simple().then(console.log)
// The equivalent code explicitly returning a Promise - this is what `async` does
function simpleP(n = 0) {
return new Promise(resolve => resolve(n+1))
}
// Proof that it behaves the same
simpleP().then(console.log)
// Promise composition
simple()
.then(simpleP)
.then(console.log)
// Same functionality, using async/await
async function main() {
const num = await simple()
const res = await simpleP(num)
console.log(`${num} + 1 = ${res}`)
}
main()
Marking synchronous return functions async makes them composable with async code that actually needs a Promise to return a value. You can use it to lift your functions to the same async context and compose them.
// Synchronous function that is Promisified and composable
async function simple() {
return 3
}
To actually get a value out of an asynchronous function that takes a callback and provides its return via that, you must construct and return a Promise from your function, and then call the Promise resolve inside the callback that provides the value you want to communicate out.
function getDBRecordP(id) {
return new Promise((resolve, reject) =>
dbLib.get(id,
(err, result) => err ?
reject(err) :
resolve(result)))
}
In this case, you have to explicitly wire up the Promise machinery. This is pretty much the only time that you need to create and return a Promise.
What it is doing is wrapping a callback interface for you.
function getDBRecordP(id) {
return new Promise((resolve, reject) =>
dbLib.get(id,
(err, result) => err ?
reject(err) :
resolve(result)))
}
Notice in the callback example, you had to take a callback argument from the caller as a parameter, and then call that with the return value inside the callback that provides the value.
With the Promise machinery, you do take a callback argument, but it is not provided by the caller, it is provided by the Promise that you construct. And then you return the Promise to the caller.
The getDBRecordP function above is the essence of "how do I convert a function that takes a callback into a function that returns a Promise?".
The caller then passes in the callback to the the Promise.then() method.
So you have a machine that wraps the callback passing convention into a formal API that is composable, which callbacks are not.
The other aspect of the Promise machine is the .catch() method.
Whereas with callbacks, the convention has always been that a callback signature is (err, result), the Promise machine allows you to provide two callbacks - one for err and one for result.
Please utilize await keyword that is beautiful for async function. Your waitSec function should return a promise, also when you are resolving a promise, your value comes in .then((res)=> console.log(res)), use it for chaining like below:
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
return new Promise((resolve, reject) => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function() {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
async function whosOnFirst(i) {
const resp = await waitSec("who's on first", 1);
return resp;
}
// What's on Second - run after completion of whosOnFirst() using return for param
async function whatsOnSecond(i) {
const resp = await waitSec("what's on second", i);
return resp;
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
async function iDunnosOnthird(i) {
const resp = await waitSec("iDunno's on third", i);
return resp;
}
whosOnFirst(1).then((firstResp) => {
whatsOnSecond(firstResp).then((secResp) => {
iDunnosOnthird(secResp).then((thirdResp) => {
console.log(thirdResp);
})
})
})
You can also use the values in chain like below with async/await:
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
return new Promise((resolve, reject) => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function() {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
async function whosOnFirst(i) {
const resp = await waitSec("who's on first", 1);
return resp;
}
// What's on Second - run after completion of whosOnFirst() using return for param
async function whatsOnSecond(i) {
const resp = await waitSec("what's on second", i);
return resp;
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
async function iDunnosOnthird(i) {
const resp = await waitSec("iDunno's on third", i);
return resp;
}
let test = async() => {
var res1 = await whosOnFirst(1);
var res2 = await whatsOnSecond(res1);
var res3 = await iDunnosOnthird(res2);
console.log(res3);
}
test();
You need to await for each result like that:
async function main() {
const result1 = await whosOnFirst(1);
// whosOnSecond will not get called except after the whosOnFirst is done.
const result2 = await whosOnSecond(result1);
}
Here's how it's supposed to work:
function delay(milliseconds){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve();
}, milliseconds);
});
}
async function test(){
console.log('no delay, yet');
await delay(1000);
console.log('After 1 second');
await delay(1000);
console.log('2 seconds have passed');
await delay(3000);
console.log('Oh look, 5 seconds have passed!');
}
test();
async functions await until a Promise is resolved, then go to the next block of code after the Promise, possibly awaiting again... and again. I admit I thought it was strange that an async function really runs the awaits synchronously, but the async function itself it asynchronous... meaning another function executed immediately after an executed async function is likely to return results before the async function executes.
In regards to your example:
class Counter{
constructor(increment = 1, initially = 0){
this.increment = increment; this.count = initially;
}
inc(){
return this.count += this.increment;
}
dec(){
return this.count -= this.increment;
}
}
function delay(milliseconds){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve();
}, milliseconds);
});
}
const sec = new Counter;
function logCaller(callerName){
console.log(`${callerName} called, after ${sec.inc()} second(s)...`);
}
async function what(){
await delay(1000);
logCaller("who's on first");
await delay(1000);
logCaller("what's on second");
await delay(1000);
logCaller("iDunno's on third");
}
what();

Resolve promise on array push - javascript

I'm attempting to define a function that returns a promise. The promise should resolve when a given array is set (push()).
To do this I'm attempting to use a Proxy object (influenced by this):
let a = []
;(async function(){
const observe = array => new Promise(resolve =>
new Proxy(array, {
set(array, key, val) {
array[key] = val;
resolve();
}
}));
while(true){
await observe(a);
console.log(new Date().toLocaleTimeString(),"Blimey Guv'nor:",`${a.pop()}`);
}
})(a);
;(async function(){
await new Promise(resolve => timerID = setTimeout(resolve, 2000))
a.push('ʕ·͡ᴥ·ʔ');
a.push('¯\(°_o)/¯ ')
})(a)
I can't see why this doesn't work. Does anyone have any idea?
More generally, what is a good way to have a promise resolve on push to an array?
The problems with your attempt:
you invoke .push on the original array, not the proxied one. Where you create the proxy, it is returned to no-one: any reference to it is lost (and will be garbage collected).
The code following after the line with await will execute asynchronously, so after all of your push calls have already executed. That means that console.log will execute when the array already has two elements. Promises are thus not the right tool for what you want, as the resolution of a promise can only be acted upon when all other synchronous code has run to completion. To get notifications during the execution synchronously, you need a synchronous solution, while promises are based on asynchronous execution.
Just to complete the answer, I provide here a simple synchronous callback solution:
function observed(array, cb) {
return new Proxy(array, {
set(array, key, val) {
array[key] = val;
if (!isNaN(key)) cb(); // now it is synchronous
return true;
}
});
}
let a = observed([], () =>
console.log(new Date().toLocaleTimeString(),"Blimey Guv'nor:", `${a.pop()}`)
);
a.push('ʕ·͡ᴥ·ʔ');
a.push('¯\(°_o)/¯ ');
As noted before: promises are not the right tool when you need synchronous code execution.
When each push is executed asynchronously
You can use promises, if you are sure that each push happens in a separate task, where the promise job queue is processed in between every pair of push calls.
For instance, if you make each push call as part of an input event handler, or as the callback for a setTimeout timer, then it is possible:
function observed(array) {
let resolve = () => null; // dummy
let proxy = new Proxy(array, {
set(array, key, val) {
array[key] = val;
if (!isNaN(key)) resolve();
return true;
}
});
proxy.observe = () => new Promise(r => resolve = r);
return proxy;
}
let a = observed([]);
(async () => {
while (true) {
await a.observe();
console.log(new Date().toLocaleTimeString(),"Blimey Guv'nor:",`${a.pop()}`);
}
})();
setTimeout(() => a.push('ʕ·͡ᴥ·ʔ'), 100);
setTimeout(() => a.push('¯\(°_o)/¯ '), 100);

How to use aynsc/await in javascript for returning objects from asynchronous operations

I cannot for the life of me figure out why async/await behaves the way it does.
Consider this example.
I want a function that does some db initialization, and returns me the database object when it is done.
var globalDb = null;
const IDB_DATABASE_NAME = "mydb";
const IDB_STORE_NAME = "mystore";
const IDB_VERSION = 1.0;
async function initDb() {
var idbOpenDbRequest = window.indexedDB.open(IDB_DATABASE_NAME, IDB_VERSION);
idbOpenDbRequest.onsuccess = function (event) {
return event.target.result;
};
}
(async () => {
globalDb = await initDb();
console.log("I should happen second")
console.log(globalDb);
})();
Expected
console.log("I should happen first")
console.log("I should happen second")
console.log(globalDb); //dumps the object to console
Actual
console.log("I should happen second")
console.log(globalDb); //undefined
console.log("I should happen first")
I realize I am fundamentally misunderstanding something here. Please enlighten me why await does not work as I would expect. :)
JsFiddle
https://jsfiddle.net/p2jqyrou/2/
Ps. Forget that this is about indexedDb and that this example is extremely simplified - I don't think it matters for the topic of this question.
So the problem is with your initDb function. I'll rewrite it for you and then explain why this version does work:
function initDb() {
var idbOpenDbRequest = window.indexedDB.open(IDB_DATABASE_NAME, IDB_VERSION);
return new Promise((resolve, reject) => {
idbOpenDbRequest.onsuccess = function (event) {
setTimeout(function () {
console.log("I should happen first");
resolve(event.target.result);
}, 2000);
};
})
}
What I've done is wrap the onsuccess callback in a Promise. The async/await pattern in Javascript is fundamentally based around Promises. Either you return a Promise yourself or it wraps the result in a Promise (and immediately resolves it).
Because you did not return a Promise yourself it tried to wrap the return value (which was undefined) in a Promise. It then immediately resolved causing the async function to return.
With this updated code initDb is called which then returns a Promise which is only resolved after the connection has been made and after the timeout triggered.

How to create an async function that waits on an event in Javascript?

How do I block on the event?
const EventEmitter = require('events').EventEmitter;
const util = require('util');
function Task() {
EventEmitter.call(this);
this.setMaxListeners(Infinity);
this.dosomething = () => {
console.log("do something");
};
this.run = (iter) => {
for(i = 0; i < iter; i++) {
this.dosomething();
this.emit('someevent');
}
}
}
util.inherits(Task, EventEmitter);
task = new Task();
function do_work() {
console.log("work 1");
task.once('someevent', (props) => {
console.log('event happened');
});
console.log("work 2");
}
do_work();
task.run(5);
Acutal Result
work 1
work 2
do something
event happened
do something
do something
do something
do something
Expected Result
work 1
do something
event happened
work 2
do something
do something
do something
do something
If I understand your question correctly, then this can be achieved via a Promise that wraps the task event handler:
async function do_work() {
console.log("work 1");
// use a promise to block completion of do_work() function
// until task event callback has been invoked
await (new Promise(resolve => {
task.once('someevent', (props) => {
console.log('event happened');
// Now that task callback has been invoked, "resolve" the
// enclosing promise to allow do_work()'s execution to complete
resolve();
});
}));
console.log("work 2");
}
The idea in the code above is to wrap the task someevent handler so that the promise resolve can be invoked once the event handler has fired (ie by calling resolve()). This allows the call to do_work() to resume, to achieve the desired execution behavior.
Also, you will need to do something like this:
// Because do_work() is async, and becase you want to ensure that
// do_work() completes before starting task.run(5), you need to enclose
// these in an async function, eg doWorkThenRunTasks()
async function doWorkThenRunTasks() {
// Call do_work asynchronously
await do_work()
// task.run will happen after do_work completed
task.run(5);
}
doWorkThenRunTasks();
The addition of the async doWorkThenRunTasks() function allows you to use await in relation to do_work() to enforce execution order of task.run(5) after completion of do_work().
Hope this helps!

Promise not waiting on one another

So my understanding of promises lead me to believe that my other promises would run one after another in my then chain but I'm doing something wrong here.
The code I'm using currently is as follows
const mainPromise = () => Promise.resolve(
console.log('spinner spinning...')
...spinner code.... //this is omitted code
);
const getData = () => Promise.resolve(
someObj.getProducts('data.json')
);
const updateProduct = () => Promise.resolve(
setTimeout(()=>{
someObj.updateProductHTML()
}, 0)
);
const updateDom = () => {
setTimeout(()=>{
someObj.updateDOM()
}, 0)
};
and my promise chain
mainPromise()
.then(getData)
.then(updateProduct)
.then(updateDom)
;
They seem to be initially running in order but the Ajax call I have in getProducts also has a for loop worker to build my array of objects and is finishing after all my .thens run.
I am attempting to at least have my data call and worker finish before updateProduct and updateDOM runs
--- UPDATE ---
ok so with the revised promises set up as such as per suggestions in the comments and Samanime's answer
const mainPromise = () => Promise.resolve(
console.log('spinner spinning...')
);
const getData = () => new Promise( resolve => {
console.log('getData');
someObj.getProducts('data.json');
resolve();
}
);
const updateProduct = () => new Promise( resolve =>{
console.log('updateProduct');
someObj.updateProductHTML();
resolve();
});
//execute promise chain
mainPromise()
.then(getData)
.then(updateProduct)
.then(page.updateDOM)
;
I updated the promises to not immediately resolve and am attempting to call resolve after I call my functions ( although I'm uneasy as to if resolve will be called before or after these functions ).
Unfortunately, I'm still getting the same behavior. I've added console logs to my functions as well as my promises and I'm getting this list back
log.spinner spinning
log.getData
log.updateProduct
log.A log from the function updateProduct calls
log.48 product objects created (for loop worker in my getProducts function)
log.Data retrieved and assigned
the last two logs would ideally be called after getData
None of the calls or functions outside of the ones provided are return promises, I'm working on legacy code and I'm moving away from the setTimeout trick as well as my results weren't consistent.
--UPDATE 2 --
The problem I'm having is known as Forking/Splitting. I just need to figure out chaining specifically to fix my issue.
-- FINAL --
this is what I ended up working out
// execute promise chain
mainPromise()
.then(getData);
//the timeout is a little hack to ensure the sequence is kept
mainPromise()
.then(() => {
setTimeout(() => {
myObj.updateProductHTML();
myObj.updateDOM();
}, 0);
});
apparently .then(foo).then(bar) just runs foo and bar at the same time
seems to be working ok right but I feel like somethings not right with it still.
I believe it's because Promise.resolve() doesn't do quite what you think it does.
Promise.resolve() creates a new Promise and resolves it immediately using the value of what it's given. Things like setTimeout return their id (an integer) immediately, so they aren't doing what you want. Your getProducts() is probably an async call, so it may be returning null or something as well (if it's returning a Promise or returns the value synchronously, then it's fine).
You're better off writing a normal Promise and calling resolve() at the appropriate time.
const mainPromise = () => Promise.resolve(
console.log('spinner spinning...')
...spinner code....
);
// Assuming it's already returning a Promise or synchronous response. If it isn't, then deal with it like the setTimeout ones below.
const getData = () => someObj.getProducts('data.json')
const updateProduct = () => new Promise(resolve => {
setTimeout(()=>{
someObj.updateProductHTML();
resolve();
}, 0)
});
// You don't NEED to in your example since it's at the end of the chain, but you probably want to wrap this too in case you add to the chain.
const updateDom = () => new Promise(resolve => {
setTimeout(()=>{
someObj.updateDOM();
resolve();
}, 0)
});

Categories

Resources