I am experimenting with Promise from ES6, but I can't find any alternative to complete as in jQuery ajax. I need to execute function after all the registered handlers with "then".
Thanks!
As mentioned by Bergi, what you want is the disposer pattern. Your central conception of a promise appears to be a bit off, and I think that is making this harder for you to reason about. When you call .then, you are not conceptually "attaching a handler", you are creating a new promise that will by definition resolve after all of its .then handlers have run.
Given your central issue based on code like this:
// a.js
module.exports = function(){
// Where 'Promise.resolve()' is a stand in for your ajax.
return Promise.resolve()
.then(function(){
// Want this to run after 'B'.
});
}
// b.js
var makePromise = require('./a');
module.exports = function specialMakePromise(){
return makePromise().then(function(){
// Should run first.
});
}
They will always run in the wrong order, because by definition, the .then handler from a.js must run and complete before the .then handler from b.js.
One way to approach this problem would instead to structure your code like this:
// a.js
module.exports = function(callback){
return Promise.resolve()
.then(callback)
.then(function(){
// Want this to run after 'B'.
});
}
// b.js
var makePromise = require('./a');
module.exports = function specialMakePromise(){
return makePromise(function(){
// Should run first.
});
}
Related
Let's assume I have a service function that returns me the current location. And the function has callbacks to return the location. We can easily mock the function like as follows. But I wanted to introduce some delay (let's say 1 sec) before the callFake() invokes the successHandler(location).
Is there a way to achieve that?
xxxSpec.js
spyOn(LocationService, 'getLocation').and.callFake(function(successHandler, errorHandler) {
//TODO: introduce some delay here
const location = {...};
successHandler(location);
}
LocationService.js
function getLocation(successCallback, errorCallback) {
let location = {...};
successCallback(location);
}
Introducing delay in Javascript is easily done with the setTimeout API, details here. You haven't specified if you are using a framework such as Angular, so your code may differ slightly from what I have below.
It does not appear that you are using Observables or Promises for easier handling of asynchronous code. Jasmine 2 does have the 'done' callback that can be useful for this. Something like this could work:
it( "my test", function(done) {
let successHandler = jasmine.createSpy();
spyOn(LocationService, 'getLocation').and.callFake(function(successHandler, errorHandler) {
setTimeout(function() {
const location = {...};
successHandler(location);
}, 1000); // wait for 1 second
})
// Now invoke the function under test
functionUnderTest(/* location data */);
// To test we have to wait until it's completed before expecting...
setTimeout(function(){
// check what you want to check in the test ...
expect(successHandler).toHaveBeenCalled();
// Let Jasmine know the test is done.
done();
}, 1500); // wait for longer than one second to test results
});
However, it is not clear to me why adding the timeouts would be valuable to your testing. :)
I hope this helps.
I am currently writing some tests in a legacy system and am confused about a test result here.
I have one test here which fails as expected but mocha shows as a result 1 passing and 1 failing.
I am using Bluebird Promises, mocha, chai-as-promised and sinon with sinon-chai for spies and stubs. This is the test (I have removed stuff not needed for understanding my problem):
describe("with a triggerable activity (selection)", function () {
beforeEach(function stubData() {
stubbedTriggerFunction = sinon.stub(trigger, "newParticipation");
activityLibStub = ... // stub
selectionLibStub = ... // stub
fakeActivities = ... // simple data with just ONE element
fakeSelection = ... // simple data with just ONE element
// stub methods to return synthetic data
activityLibStub.returns(new Promise(function (resolve) {
resolve(fakeActivities);
}));
selectionLibStub.withArgs(1).returns(new Promise(function (resolve) {
resolve(fakeSelection);
}));
});
afterEach(function releaseStubs() {
// restore stubs...
});
it("should call the newParticipation function", function () {
var member = memberBuilder.buildCorrect();
trigger.allActivities(member)
.then(function () {
return expect(stubbedTriggerFunction).to.not.have.been.called;
})
.done();
})
});
This test fails as expected, because the function is actually invoked. However, mocha tells me that one test passed and one test failed. This is the only test I have implemented in this module.
I am pretty sure this has something to do with the promises but I don't seem to figure out what it is. Also if I add
.catch(function(){
chai.assert.fail();
})
after the then-block, mocha still claims that one test passed. The method is also just invoked one time and I have only one synthetic dataset which I am working with. So I don't see what it is which tells mocha that this has passed while failed...
Any ideas?
Regards, Vegaaaa
Return your promise, return your promise, return your promise. Let's chant it all together now "Return, yoooooooour promise!"
it("should call the newParticipation function", function () {
var member = memberBuilder.buildCorrect();
return trigger.allActivities(member)
.then(function () {
return expect(stubbedTriggerFunction).to.not.have.been.called;
});
})
});
I've also removed .done() because that's not generally useful with Bluebird and would be downright harmful here. Mocha still needs to use your promise.
(It's use is generally discouraged, see here.) If you do not return your promise, then Mocha treats you test as synchronous and most likely that's going to be successful because your test is not really testing anything synchronously. Then if you get an asychronous failure, Mocha has to decide what failed exactly and will do its best to record the failure but it can lead to funny things like having an incorrect number of tests or the same test being reported as failed and successful!
So i have these 2 functions loadScriptWithBluebird and loadBluebird,
loadScriptWithBluebird is supposed to be generic for the main flow of the application, and will always return a promise. I wonder if it is possible with some kind of a pattern to use loadBluebird inside loadScriptWithBluebird
function loadBluebird(callback){
//load the script and callback ...
script.onload = function() {
callback();
};
}
function loadScriptWithBluebird (src){
if(isBluebirdAvailable()){
return new Bluebird(function(resolve, reject){
//load the script and resolve ...
script.onload = function() {
resolve();
};
});
}else{
//issue starts here since i obviously cannot return inside the callback
loadBluebird(function(){
loadScriptWithBluebird(src)
})
}
},
So the flow of the application will look like this :
loadScriptWithBluebird('jquery').then(function(){
//...
});
Thanks.
No, you can't do that, since bluebird wouldn't yet be available when you need to return the promise. So simply ignore that, and rely on the caller of your loadScript function to already have loaded Bluebird. There's not really a way around this.
I'm trying to create a amd module that runs a d3 request (d3.json() in this case) and returns the data from the request. I can't seem to figure out how to make the module wait for the request to finish before it returns the data. As a result I keep getting undefined in my main program when I try to access the data.
define(['app/args'], function(args){
d3.json("resources/waterData.php?stn=" + args.stationID, function (error, data) {
var dataToReturn = {};
//Do some stuff with data
return dataToReturn;
});
});
That is the basic structure of what I'm trying to do. I think the main issue is that the 2nd argument in d3.json is a callback for when the data is loaded, so when I try to return the data, it isn't getting outside the module. I haven't been able to figure out how to get the data from the callback to return it outside the module.
The real issue is that the d3.json function is asynchronous, so you can't just return the processed data from the outside function directly. One way you can work around this is by returning a promise rather than the data itself. You can use the d3.json callback to resolve the promise, and then other modules which depend on the data can register their own callbacks which will run once that promise has been resolved.
For example, if you use jQuery's $.Deferred() you can do the following:
define(['app/args', 'jquery'], function(args, $){
// CREATE DEFERRED OBJECT
var deferred = $.Deferred();
d3.json("resources/waterData.php?stn=" + args.stationID, function (error, data) {
var dataToReturn = {};
//Do some stuff with data
// RESOLVE NOW THAT THE DATA IS READY
deferred.resolve(dataToReturn);
});
// RETURN THE PROMISE
return deferred.promise();
});
Then when you want to use the data, you can require the above module, and register a listener that will fire once the data is ready:
require(['nameOfYourModuleAbove'], function(deferred) {
deferred.done(function(data) {
// DO SOMETHING WITH YOUR DATA
});
})
This is a sample functionality I need, is it possible??. I am facing problem in debugging it
I need those two functions to be ran before I do any other modification how to do it?
function getPromise() {
var deferred = $.Deferred();
$.when(getPromiseother()).done(function() {
deferred.resolve();
});
deferred.getPromiseother()
}
function getPromise() {
var deferrednext = $.Deferred();
$.when($.ajax(somefunction)).done(function() {
deferrednext.resolve();
});
deferrednext.promise()
}
$.when(getPromise).done(function() {
do something
});
This is a very clumsy way to do this - basically a major point of promises is that they compose and chain and you don't need to $.Deferred anywhere that's not converting a callback API to promises.
$.when($.ajax(somefunction), someOtherPromise).then(function(result, other){
// in here both promises have run to fulfillment and data is available
// so place the rest of your code here
});