UPDATE: Safe to say this is basically a duplicate of Jest: Timer and Promise don't work well. (setTimeout and async function)
I have a function that repeats itself after performing an asynchronous function. I want to verify that jest.advanceTimersOnTime(5000) continues to yield expect(doAsyncStuff).toHaveBeenCalledTimes(X);.
I observe that we can reach the call to repeat the function but it does not "go through" when asking jest to advance timers. It does work when you remove the async function preceding it. So it sounds like I have to get doAsyncStuff to "call through" or resolve some pending promise here?
Function
function repeatMe() {
setTimeout(() => {
doAsyncStuff.then((response) => {
if (response) {
console.log("I get here!");
repeatMe();
}
})
}, 5000);
}
Test
jest.useFakeTimers();
let doAsyncStuff = jest.spyOn(updater, 'doAsyncStuff');
doAsyncStuff.mockResolvedValue(true);
repeatMe();
jest.advanceTimersByTime(5000);
expect(doAsyncStuff).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(5000);
expect(doAsyncStuff).toHaveBeenCalledTimes(2); // Failed?
So it sounds like I have to...resolve some pending promise here?
Yes, exactly.
The short answer is that a Promise callback gets queued in PromiseJobs by the then chained to the Promise returned by doAsyncStuff, and the way the test is written, that callback never has a chance to run until the test is already over.
To fix it, give the Promise callbacks a chance to run during your test:
updater.js
export const doAsyncStuff = async () => { };
code.js
import { doAsyncStuff } from './updater';
export function repeatMe() {
setTimeout(() => {
doAsyncStuff().then((response) => {
if (response) {
console.log("I get here!");
repeatMe();
}
})
}, 5000);
}
code.test.js
import * as updater from './updater';
import { repeatMe } from './code';
test('repeatMe', async () => {
jest.useFakeTimers();
let doAsyncStuff = jest.spyOn(updater, 'doAsyncStuff');
doAsyncStuff.mockResolvedValue(true);
repeatMe();
jest.advanceTimersByTime(5000);
expect(doAsyncStuff).toHaveBeenCalledTimes(1); // Success!
await Promise.resolve(); // let callbacks in PromiseJobs run
jest.advanceTimersByTime(5000);
expect(doAsyncStuff).toHaveBeenCalledTimes(2); // Success!
await Promise.resolve(); // let callbacks in PromiseJobs run
jest.advanceTimersByTime(5000);
expect(doAsyncStuff).toHaveBeenCalledTimes(3); // Success!
// ... and so on ...
});
The complete details of exactly what happens and why can be found in my answer here
Apparently Jest troubleshooting references this issue: we have set the definition to happen asynchronously on the next tick of the event loop. I guess this applies to events within the live code too, not just the written tests.
Doing jest.runAllTimers() will set things into an endless loop.
Adding jest.runAllTicks() will advance the tick so the above tests now work.
I'm still confused but I guess this is the answer.
jest.advanceTimersByTime(5000);
jest.runAllTicks();
expect(doAsyncStuff).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(5000);
jest.runAllTicks();
expect(doAsyncStuff).toHaveBeenCalledTimes(2);
https://jestjs.io/docs/en/troubleshooting#defining-tests
Also required for this to work is to mock the implementation of doAsyncStuff because whatever async stuff inside doAsyncStuff also I think gets queued into the tick events.
doAsyncStuff.mockImplementation(() => {
return new Promise.resolve();
});
How do use Jest to mock timer context.getRemaining() and mock sendMessage() when it has been called?
I want context.getRemaining() to be decreased by 9sec when sendMessage()has been called. sendMessage() will be called multiple time via while loop. It will start with 60,000ms.
exports.handler = async (context) => {
while (true) {
console.log(context.getRemaining());
await sendMessage();
if (context.getRemaining() < 13000) {
break;
}
}
return "FINISH";
}
You can use jest.fn() for context.getRemaining and provide multiple functions for it:
sendMessage = jest.fn(async () => {});
await handler({
getRemaining: jest.fn()
.mockReturnValueOnce(20000)
.mockReturnValueOnce(10000),
});
expect(sendMessage).toHaveBeenCalledTimes(2)
When you write tests, usually it's better to avoid complex logic in the test code. For the mocks you can provide explicit return values. Else you'll need to write tests for your test code, which is bad.
I'm trying to write a plugin for Jasmine that allows you to return a promise from a spec and will pass or fail that spec depending on whether or not the promise is fulfilled or rejected.
Of course, I want to write tests to make sure that my plugin works correctly, and to be thorough, I need to make sure that tests fail when the promise is rejected... so how do I make a test pass when I need to make sure that a test "successfully fails"?
After a conversation with the developers who work on Jasmine, we've come up with this:
var FAILED = 'failed'
var PASSED = 'passed'
describe('My Test Suite', function () {
var env
beforeEach(function () {
// Create a secondary Jasmine environment to run your sub-specs in
env = new jasmine.Env()
})
it('should work synchronously', function () {
var spec
// use the methods on `env` rather than the global ones for sub-specs
// (describe, it, expect, beforeEach, etc)
env.describe('faux suite', function () {
spec = env.it('faux test', function (done) {
env.expect(true).toBe(true)
})
})
// this will fire off the specs in the secondary environment
env.execute()
// put your expectations here if the sub-spec is synchronous
// `spec.result` has the status information we need
expect(spec.result.status).toBe(FAILED)
})
// don't forget the `done` argument for asynchronous specs
it('should work asynchronously', function (done) {
var spec
// use the methods on `env` rather than the global ones.
env.describe('faux suite', function () {
// `it` returns a spec object that we can use later
spec = env.it('faux test', function (done) {
Promise.reject("FAIL").then(done)
})
})
// this allows us to run code after we know the spec has finished
env.addReporter({jasmineDone: function() {
// put your expectations in here if the sub-spec is asynchronous
// `spec.result` has the status information we need
expect(spec.result.status).toBe(FAILED)
// this is how Jasmine knows you've completed something asynchronous
// you need to add it as an argument to the main `it` call above
done()
}})
// this will fire off the specs in the secondary environment
env.execute()
})
})
Going off Joe's answer, I moved the fake test context into a single function. Since the code under test is making use of jasmine expectations, I load the inner Env into jasmine.currentEnv_ and call it explicitly with jasmine.currentEnv_.expect(). Note that currentEnv_ is an internal variable set by jasmine itself, so I can't guarantee that this won't be broken in a future jasmine version.
function internalTest(testFunc) {
var outerEnvironment = jasmine.currentEnv_;
var env = new jasmine.Env();
jasmine.currentEnv_ = env;
var spec;
env.describe("fake suite", function () {
spec = env.it("fake test", function () {
func();
});
});
env.execute();
jasmine.currentEnv_ = outerEnvironment;
return spec.result;
}
Then each test looks like
it("does something", function () {
//Arrange
//Act
var result = internalTest(function () {
//Perform action
});
//Assert
expect(result.status).toBe("failed"); //Or "success"
expect(result.failedExpectations.length).toBe(1);
expect(result.failedExpectations[0].message).toBe("My expected error message");
});
This question is somewhat academic in that I don't have a real need to do this.
I'm wondering if I can force the resolution of a promise into a returned value from a function such that the function callers are not aware that the functions contain promised async operations.
In .NET I can do things like this by using functions on Task[] or return Task.Result which causes the caller to await the completion of the task and callers won't know or care that the work has been done using tasks.
If you're using ES6 you can use a generator to make code like this. It essentially comes close to 'blocking' on the promise, so you have the appearance of a long-running method that just returns the value you want, but async/promises live under the covers.
let asyncTask = () =>
new Promise(resolve => {
let delay = Math.floor(Math.random() * 100);
setTimeout(function () {
resolve(delay);
}, delay);
});
let makeMeLookSync = fn => {
let iterator = fn();
let loop = result => {
!result.done && result.value.then(res =>
loop(iterator.next(res)));
};
loop(iterator.next());
};
makeMeLookSync(function* () {
let result = yield asyncTask();
console.log(result);
});
More explanation and the source available here: http://www.tivix.com/blog/making-promises-in-a-synchronous-manner/
Here is the code compiled on Babeljs.io
Suppose you maintain a library that exposes a function getData. Your users call it to get actual data:
var output = getData();
Under the hood data is saved in a file so you implemented getData using Node.js built-in fs.readFileSync. It's obvious both getData and fs.readFileSync are sync functions. One day you were told to switch the underlying data source to a repo such as MongoDB which can only be accessed asynchronously. You were also told to avoid pissing off your users, getData API cannot be changed to return merely a promise or demand a callback parameter. How do you meet both requirements?
Asynchronous function using callback/promise is the DNA of JavasSript and Node.js. Any non-trivial JS app is probably permeated with this coding style. But this practice can easily lead to so called callback pyramid of doom. Even worse, if any code in any caller in the call chain depends on the result of the async function, those code has to be wrapped in callback function as well, imposing a coding style constraint on caller. From time to time I find the need to encapsulate an async function (often provided in a 3rd party library) into a sync function in order to avoid massive global re-factoring. Searching for a solution on this subject usually ended up with Node Fibers or npm packages derived from it. But Fibers just cannot solve the problem I am facing. Even the example provided by Fibers' author illustrated the deficiency:
...
Fiber(function() {
console.log('wait... ' + new Date);
sleep(1000);
console.log('ok... ' + new Date);
}).run();
console.log('back in main');
Actual output:
wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
back in main
ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)
If function Fiber really turns async function sleep into sync, the output should be:
wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)
back in main
I have created another simple example in JSFiddle and looking for code to yield expected output. I'll accept a solution that only works in Node.js so you are free to require any npm package despite not working in JSFiddle.
deasync turns async function into sync, implemented with a blocking mechanism by calling Node.js event loop at JavaScript layer. As a result, deasync only blocks subsequent code from running without blocking entire thread, nor incuring busy wait. With this module, here is the answer to the jsFiddle challenge:
function AnticipatedSyncFunction(){
var ret;
setTimeout(function(){
ret = "hello";
},3000);
while(ret === undefined) {
require('deasync').runLoopOnce();
}
return ret;
}
var output = AnticipatedSyncFunction();
//expected: output=hello (after waiting for 3 sec)
console.log("output="+output);
//actual: output=hello (after waiting for 3 sec)
(disclaimer: I am the co-author of deasync. The module was created after posting this question and found no workable proposal.)
You've got to use promises:
const asyncOperation = () => {
return new Promise((resolve, reject) => {
setTimeout(()=>{resolve("hi")}, 3000)
})
}
const asyncFunction = async () => {
return await asyncOperation();
}
const topDog = () => {
asyncFunction().then((res) => {
console.log(res);
});
}
I like arrow function definitions more. But any string of the form "() => {...}" could also be written as "function () {...}"
So topDog is not async despite calling an async function.
EDIT: I realize a lot of the times you need to wrap an async function inside a sync function is inside a controller. For those situations, here's a party trick:
const getDemSweetDataz = (req, res) => {
(async () => {
try{
res.status(200).json(
await asyncOperation()
);
}
catch(e){
res.status(500).json(serviceResponse); //or whatever
}
})() //So we defined and immediately called this async function.
}
Utilizing this with callbacks, you can do a wrap that doesn't use promises:
const asyncOperation = () => {
return new Promise((resolve, reject) => {
setTimeout(()=>{resolve("hi")}, 3000)
})
}
const asyncFunction = async (callback) => {
let res = await asyncOperation();
callback(res);
}
const topDog = () => {
let callback = (res) => {
console.log(res);
};
(async () => {
await asyncFunction(callback)
})()
}
By applying this trick to an EventEmitter, you can get the same results. Define the EventEmitter's listener where I've defined the callback, and emit the event where I called the callback.
If function Fiber really turns async function sleep into sync
Yes. Inside the fiber, the function waits before logging ok. Fibers do not make async functions synchronous, but allow to write synchronous-looking code that uses async functions and then will run asynchronously inside a Fiber.
From time to time I find the need to encapsulate an async function into a sync function in order to avoid massive global re-factoring.
You cannot. It is impossible to make asynchronous code synchronous. You will need to anticipate that in your global code, and write it in async style from the beginning. Whether you wrap the global code in a fiber, use promises, promise generators, or simple callbacks depends on your preferences.
My objective is to minimize impact on the caller when data acquisition method is changed from sync to async
Both promises and fibers can do that.
There is a npm sync module also. which is used for synchronize the process of executing the query.
When you want to run parallel queries in synchronous way then node restrict to do that because it never wait for response. and sync module is much perfect for that kind of solution.
Sample code
/*require sync module*/
var Sync = require('sync');
app.get('/',function(req,res,next){
story.find().exec(function(err,data){
var sync_function_data = find_user.sync(null, {name: "sanjeev"});
res.send({story:data,user:sync_function_data});
});
});
/*****sync function defined here *******/
function find_user(req_json, callback) {
process.nextTick(function () {
users.find(req_json,function (err,data)
{
if (!err) {
callback(null, data);
} else {
callback(null, err);
}
});
});
}
reference link: https://www.npmjs.com/package/sync
Nowadays this generator pattern can be a solution in many situations.
Here an example of sequential console prompts in nodejs using async readline.question function:
var main = (function* () {
// just import and initialize 'readline' in nodejs
var r = require('readline')
var rl = r.createInterface({input: process.stdin, output: process.stdout })
// magic here, the callback is the iterator.next
var answerA = yield rl.question('do you want this? ', r=>main.next(r))
// and again, in a sync fashion
var answerB = yield rl.question('are you sure? ', r=>main.next(r))
// readline boilerplate
rl.close()
console.log(answerA, answerB)
})() // <-- executed: iterator created from generator
main.next() // kick off the iterator,
// runs until the first 'yield', including rightmost code
// and waits until another main.next() happens
I can't find a scenario that cannot be solved using node-fibers. The example you provided using node-fibers behaves as expected. The key is to run all the relevant code inside a fiber, so you don't have to start a new fiber in random positions.
Lets see an example: Say you use some framework, which is the entry point of your application (you cannot modify this framework). This framework loads nodejs modules as plugins, and calls some methods on the plugins. Lets say this framework only accepts synchronous functions, and does not use fibers by itself.
There is a library that you want to use in one of your plugins, but this library is async, and you don't want to modify it either.
The main thread cannot be yielded when no fiber is running, but you still can create plugins using fibers! Just create a wrapper entry that starts the whole framework inside a fiber, so you can yield the execution from the plugins.
Downside: If the framework uses setTimeout or Promises internally, then it will escape the fiber context. This can be worked around by mocking setTimeout, Promise.then, and all event handlers.
So this is how you can yield a fiber until a Promise is resolved. This code takes an async (Promise returning) function and resumes the fiber when the promise is resolved:
framework-entry.js
console.log(require("./my-plugin").run());
async-lib.js
exports.getValueAsync = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve("Async Value");
}, 100);
});
};
my-plugin.js
const Fiber = require("fibers");
function fiberWaitFor(promiseOrValue) {
var fiber = Fiber.current, error, value;
Promise.resolve(promiseOrValue).then(v => {
error = false;
value = v;
fiber.run();
}, e => {
error = true;
value = e;
fiber.run();
});
Fiber.yield();
if (error) {
throw value;
} else {
return value;
}
}
const asyncLib = require("./async-lib");
exports.run = () => {
return fiberWaitFor(asyncLib.getValueAsync());
};
my-entry.js
require("fibers")(() => {
require("./framework-entry");
}).run();
When you run node framework-entry.js it will throw an error: Error: yield() called with no fiber running. If you run node my-entry.js it works as expected.
You shouldn't be looking at what happens around the call that creates the fiber but rather at what happens inside the fiber. Once you are inside the fiber you can program in sync style. For example:
function f1() {
console.log('wait... ' + new Date);
sleep(1000);
console.log('ok... ' + new Date);
}
function f2() {
f1();
f1();
}
Fiber(function() {
f2();
}).run();
Inside the fiber you call f1, f2 and sleep as if they were sync.
In a typical web application, you will create the Fiber in your HTTP request dispatcher. Once you've done that you can write all your request handling logic in sync style, even if it calls async functions (fs, databases, etc.).
I struggled with this at first with node.js and async.js is the best library I have found to help you deal with this. If you want to write synchronous code with node, approach is this way.
var async = require('async');
console.log('in main');
doABunchOfThings(function() {
console.log('back in main');
});
function doABunchOfThings(fnCallback) {
async.series([
function(callback) {
console.log('step 1');
callback();
},
function(callback) {
setTimeout(callback, 1000);
},
function(callback) {
console.log('step 2');
callback();
},
function(callback) {
setTimeout(callback, 2000);
},
function(callback) {
console.log('step 3');
callback();
},
], function(err, results) {
console.log('done with things');
fnCallback();
});
}
this program will ALWAYS produce the following...
in main
step 1
step 2
step 3
done with things
back in main
Making Node.js code sync is essential in few aspects such as database. But actual advantage of Node.js lies in async code. As it is single thread non-blocking.
we can sync it using important functionality Fiber()
Use await() and defer ()
we call all methods using await(). then replace the callback functions with defer().
Normal Async code.This uses CallBack functions.
function add (var a, var b, function(err,res){
console.log(res);
});
function sub (var res2, var b, function(err,res1){
console.log(res);
});
function div (var res2, var b, function(err,res3){
console.log(res3);
});
Sync the above code using Fiber(), await() and defer()
fiber(function(){
var obj1 = await(function add(var a, var b,defer()));
var obj2 = await(function sub(var obj1, var b, defer()));
var obj3 = await(function sub(var obj2, var b, defer()));
});
I hope this will help. Thank You