I have the following files:-
target.js
var target = function(repository, logger) {
return {
addTarget : function(target) {
repository.add(target).then(
function (newTarget) {
console.log("added");
logger.info("added");
},
function (err) {
console.log("error");
logger.info("error");
}
);
}
};
};
module.exports = target;
targetTest.js
var chai = require("chai"),
expect = chai.expect,
sinon = require("sinon"),
Promise = require("bluebird"),
baseTarget = require("../target");
describe("target", function(){
it("should log error when it occurs", function() {
var mockRepository = {
add : sinon.stub().returns(Promise.reject(new Error()))
};
var mockLogger = {
info : sinon.spy()
};
var target = baseTarget(mockRepository, mockLogger);
target.addTarget("target");
expect(mockLogger.info.calledWith("error")).to.be.true;
});
});
The issue I have is that expect(mockLogger.info.calledWith("error")).to.be.true; returns false because add method on the repository is async and so hasn't executed yet. Is there a pattern for doing this properly.
This is really more of a question about 'how Promises work' than how they work within test frameworks - the answer to which is that their behaviour remains exactly the same.
Is there a pattern for doing this properly.
It is not so much a pattern as it is what Promises are built to do. Each success handler of a then is executed in sequence on success of the last. In your code we can return the Promise created by calling repository#add as you would if you wanted to use its result or perform some external dependent operation outside of addTarget:
addTarget: function (target) {
return repository
// ^^^^^^
.add(target)
.then(function (newTarget) {
console.log("added");
logger.info("added");
}, function (err) {
console.log("error");
logger.info("error");
});
}
Then place your expectation inside a then that will be executed on success of all members of the Promise chain created in addTarget:
target.addTarget("target").then(function () {
expect(mockLogger.info.calledWith("error")).to.be.true;
cb();
});
Asynchronous Tests
You will notice in the example above that there is also a call to a function cb. Due to your test being asynchronous you need to 'tell' the test framework when the test has completed. This is most often done by declaring your it function with a parameter, from which the framework will infer that the test is asynchronous and pass in a callback:
describe("target", function () {
it("should log error when it occurs", function (cb) {
// ^^^^
});
});
i'm working on an AngularJS app where Factories (that query the DB) returns promises.
The call to those Factories are encapsulated in functions such as :
function getUsers(id) {
MyUserFactory.getUsers(id).then(function(data) {
$scope.mydata = data;
...lot of code...
})
}
function updateAcordingToUserAndWeatherOfTheDay(day) {
MyWeatherFactory.getWeather(day).then(function(data) {
if ($scope.mydata.likesRain) {
...
}
})
}
then I have a :
getUser(42); updateAcordingToUserAndWeatherOfTheDay(monday);
Obviously, this works one out of two time, if the async query done by getUser() didn't had time to complete I get an error in updateAcordingToUserAndWeatherOfTheDay() on the $scope.mydata.likesRain
I know I could chain the .then() to force one query to wait for the other but
this refactoring would take a lot of time I don't have right now.
Is there another way I can wait for a promise to be completed ? At least to temporary fix the bug before a refactoring. Now the solution we have is a 3 second timer...
Refactoring this is very simple, just have the method return the promise.
function getUsers(id) {
return MyUserFactory.getUsers(id).then(function(data) {
$scope.mydata = data;
...lot of code...
})
}
function updateAcordingToUserAndWeatherOfTheDay(day) {
return MyWeatherFactory.getWeather(day).then(function(data) {
if ($scope.mydata.likesRain) {
...
}
})
}
getUsers(42).then(function() { updateAcordingToUserAndWeatherOfTheDay(day); });
This doesn't have to change any existing code (because returning the promise doesn't break any existing infrastructure), other than the code that you need to modify.
This is the fastest way to refactor to make it work.
.then() is probably the right way to go. Other solution using callbacks:
function getUsers(id, callback) {
MyUserFactory.getUsers(id).then(function(data) {
$scope.mydata = data;
...lot of code...
if (typeof callback === "function") {
callback();
}
})
}
getUser(42, function() {
updateAcordingToUserAndWeatherOfTheDay(monday);
});
I have a question about promises.
I am using Bluebird Promise library and build a small async library with it.
I am trying to waterfall promises with the use of a function.
Say I use promises like so:
Promise.resolve()
.then(function () {
console.log("called 1")
return 1;
}).then(function () {
return new Promise (function (res, rej) {
setTimeout(function () {
console.log("called 2")
res(2);
}, 1500);
});
}).then(function () {
console.log("called 3")
return 3;
});
This does in fact wait in a loop and return 1,2,3 in order.
How do I wrap it into a function so that I can do something like this:
a();b();c();, or a().b().c(); where a() puts something onto a chain, b() puts something onto a chain, and c() puts something onto a chain in order.
Since then() returns a new promise, it can all go out of order, so something like
does not work:
var promise = Promise.resolve();
function a () {
promise.then(function () {
// do sync/async
});
}
function b () {
promise.then(function () {
//do sync/async
});
}
function c ...
Thank you for your time :]
I'm not sure what the goal is here. Do you want to have an arbitrary number of things run in sequence where the sequence is known in advance? Or is this a case where the sequence is discovered as you go? The NodeJS streams interface is a lot better for processing an unknown number of things sequentially (#tadman)
Sequence is discoverable, goal is to have ability to call a().b().c() or b().a().d(). Async library on a client-side.
Update: If I do as #zerkms says it does not work as expected. My bad, should work ok, but with lack of context/code did not give me enough info to expand on. Still thank you for your answer, as it gave me more food for thought.
Update: See my answer
You could use a scoped prototype and just add those methods there
Promise.prototype.a = function() {
return this.then(function() {
console.log("called 1")
return 1;
});
};
Promise.prototype.b = function() {
return this.delay(1500).then(function() {
console.log("called 2")
return 1;
});
};
Promise.prototype.c = function() {
return this.then(function() {
console.log("called 3")
return 3;
});
};
I use this to create neat DSLs e.g. with git:
https://gist.github.com/petkaantonov/6a73bd1a35d471ddc586
Thanks to #tadman I came up with this so far, seems to work as I expect it to.
The problem was that I did not update the promise before calling then on it, and it was branching instead of calling it in sequence.
And this is what I wanted - to turn an object that has both sync/async into async to allow chaining. Petka (#Esailija) also shows great example of building DSLs above (semvar version bumping & git pushing) by extending bluebird library, but for my purposes this is enough.
var Sample = function () {
this.promise = Promise.resolve();
};
Sample.prototype.a = function () {
this.then(function () {
console.log("1");
});
return this;
};
Sample.prototype.b = function () {
this.then(function () {
return new Promise(function (res, rej) {
setTimeout(function() {
console.log("2");
res();
}, 500);
});
});
return this;
};
Sample.prototype.c = function () {
this.then(function () {
console.log("3");
})
return this;
};
Sample.prototype.chainPromise = function (func) {
this.promise = this.promise.then(func);
};
var s = new Sample();
s.a().b().c();
or even then instead of chainPromise?
Sample.prototype.then = function (func) {
this.promise = this.promise.then(func);
return this.promise;
};
So let's say I have this code:
function DBManager()
{
this.getContactsList = function(cb)
{
$.post('/post/getContactsList', function (contacts) {
cb(contacts);
});
}
}
Then this:
var DBManager = new DBManager();
DBManager.getContactsList(function (contacts) {
console.log(contacts);
});
I actually have a lot more functions inside DBManager, and the above code with the callbacks seems redundant so I was wondering if there would be more optimal ways to make calls to DBManager?
You could use promises in JavaScript to accomplish the same functionality provided by callbacks. If you are using RSVP.js or something similar.
function DBManager()
{
this.getContactsList = function()
{
return $.post('/post/getContactsList');
}
}
var DBManager = new DBManager();
DBManager.getContactsList().then(function (contacts) {
console.log(contacts);
});
Check out this code :
Link
<span>Moving</span>
$('#link').click(function () {
console.log("Enter");
$('#link').animate({ width: 200 }, 2000, function() {
console.log("finished");
});
console.log("Exit");
});
As you can see in the console, the "animate" function is asynchronous, and it "fork"s the flow of the event handler block code. In fact :
$('#link').click(function () {
console.log("Enter");
asyncFunct();
console.log("Exit");
});
function asyncFunct() {
console.log("finished");
}
follow the flow of the block code!
If I wish to create my function asyncFunct() { } with this behaviour, how can I do it with javascript/jquery? I think there is a strategy without the use of setTimeout()
You cannot make a truly custom asynchronous function. You'll eventually have to leverage on a technology provided natively, such as:
setInterval
setTimeout
requestAnimationFrame
XMLHttpRequest
WebSocket
Worker
Some HTML5 APIs such as the File API, Web Database API
Technologies that support onload
... many others
In fact, for the animation jQuery uses setInterval.
You can use a timer:
setTimeout( yourFn, 0 );
(where yourFn is a reference to your function)
or, with Lodash:
_.defer( yourFn );
Defers invoking the func until the current call stack has cleared. Any additional arguments are provided to func when it's invoked.
here you have simple solution (other write about it)
http://www.benlesh.com/2012/05/calling-javascript-function.html
And here you have above ready solution:
function async(your_function, callback) {
setTimeout(function() {
your_function();
if (callback) {callback();}
}, 0);
}
TEST 1 (may output '1 x 2 3' or '1 2 x 3' or '1 2 3 x'):
console.log(1);
async(function() {console.log('x')}, null);
console.log(2);
console.log(3);
TEST 2 (will always output 'x 1'):
async(function() {console.log('x');}, function() {console.log(1);});
This function is executed with timeout 0 - it will simulate asynchronous task
Here is a function that takes in another function and outputs a version that runs async.
var async = function (func) {
return function () {
var args = arguments;
setTimeout(function () {
func.apply(this, args);
}, 0);
};
};
It is used as a simple way to make an async function:
var anyncFunction = async(function (callback) {
doSomething();
callback();
});
This is different from #fider's answer because the function itself has its own structure (no callback added on, it's already in the function) and also because it creates a new function that can be used.
Edit: I totally misunderstood the question. In the browser, I would use setTimeout. If it was important that it ran in another thread, I would use Web Workers.
Late, but to show an easy solution using promises after their introduction in ES6, it handles asynchronous calls a lot easier:
You set the asynchronous code in a new promise:
var asyncFunct = new Promise(function(resolve, reject) {
$('#link').animate({ width: 200 }, 2000, function() {
console.log("finished");
resolve();
});
});
Note to set resolve() when async call finishes.
Then you add the code that you want to run after async call finishes inside .then() of the promise:
asyncFunct.then((result) => {
console.log("Exit");
});
Here is a snippet of it:
$('#link').click(function () {
console.log("Enter");
var asyncFunct = new Promise(function(resolve, reject) {
$('#link').animate({ width: 200 }, 2000, function() {
console.log("finished");
resolve();
});
});
asyncFunct.then((result) => {
console.log("Exit");
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Link
<span>Moving</span>
or JSFiddle
This page walks you through the basics of creating an async javascript function.
Since ES2017, asynchronous javacript functions are much easier to write. You should also read more on Promises.
If you want to use Parameters and regulate the maximum number of async functions you can use a simple async worker I've build:
var BackgroundWorker = function(maxTasks) {
this.maxTasks = maxTasks || 100;
this.runningTasks = 0;
this.taskQueue = [];
};
/* runs an async task */
BackgroundWorker.prototype.runTask = function(task, delay, params) {
var self = this;
if(self.runningTasks >= self.maxTasks) {
self.taskQueue.push({ task: task, delay: delay, params: params});
} else {
self.runningTasks += 1;
var runnable = function(params) {
try {
task(params);
} catch(err) {
console.log(err);
}
self.taskCompleted();
}
// this approach uses current standards:
setTimeout(runnable, delay, params);
}
}
BackgroundWorker.prototype.taskCompleted = function() {
this.runningTasks -= 1;
// are any tasks waiting in queue?
if(this.taskQueue.length > 0) {
// it seems so! let's run it x)
var taskInfo = this.taskQueue.splice(0, 1)[0];
this.runTask(taskInfo.task, taskInfo.delay, taskInfo.params);
}
}
You can use it like this:
var myFunction = function() {
...
}
var myFunctionB = function() {
...
}
var myParams = { name: "John" };
var bgworker = new BackgroundWorker();
bgworker.runTask(myFunction, 0, myParams);
bgworker.runTask(myFunctionB, 0, null);
Function.prototype.applyAsync = function(params, cb){
var function_context = this;
setTimeout(function(){
var val = function_context.apply(undefined, params);
if(cb) cb(val);
}, 0);
}
// usage
var double = function(n){return 2*n;};
var display = function(){console.log(arguments); return undefined;};
double.applyAsync([3], display);
Although not fundamentally different than the other solutions, I think my solution does a few additional nice things:
it allows for parameters to the functions
it passes the output of the function to the callback
it is added to Function.prototype allowing a nicer way to call it
Also, the similarity to the built-in function Function.prototype.apply seems appropriate to me.
Next to the great answer by #pimvdb, and just in case you where wondering, async.js does not offer truly asynchronous functions either. Here is a (very) stripped down version of the library's main method:
function asyncify(func) { // signature: func(array)
return function (array, callback) {
var result;
try {
result = func.apply(this, array);
} catch (e) {
return callback(e);
}
/* code ommited in case func returns a promise */
callback(null, result);
};
}
So the function protects from errors and gracefully hands it to the callback to handle, but the code is as synchronous as any other JS function.
Unfortunately, JavaScript doesn't provide an async functionality. It works only in a single one thread. But the most of the modern browsers provide Workers, that are second scripts which gets executed in background and can return a result.
So, I reached a solution I think it's useful to asynchronously run a function, which creates a worker for each async call.
The code below contains the function async to call in background.
Function.prototype.async = function(callback) {
let blob = new Blob([ "self.addEventListener('message', function(e) { self.postMessage({ result: (" + this + ").apply(null, e.data) }); }, false);" ], { type: "text/javascript" });
let worker = new Worker(window.URL.createObjectURL(blob));
worker.addEventListener("message", function(e) {
this(e.data.result);
}.bind(callback), false);
return function() {
this.postMessage(Array.from(arguments));
}.bind(worker);
};
This is an example for usage:
(function(x) {
for (let i = 0; i < 999999999; i++) {}
return x * 2;
}).async(function(result) {
alert(result);
})(10);
This executes a function which iterate a for with a huge number to take time as demonstration of asynchronicity, and then gets the double of the passed number.
The async method provides a function which calls the wanted function in background, and in that which is provided as parameter of async callbacks the return in its unique parameter.
So in the callback function I alert the result.
MDN has a good example on the use of setTimeout preserving "this".
Like the following:
function doSomething() {
// use 'this' to handle the selected element here
}
$(".someSelector").each(function() {
setTimeout(doSomething.bind(this), 0);
});