I am having trouble testing the returned value of a function that waits for a promise to be resolved before executing.
Javascript Method (serviceClient._getProduct returns a jQuery ajax promise object)
serviceClient.getProductName = function(id, storeId) {
$.when(self._getProduct(id)).done(function(data) {
return data.name;
});
};
Test Code
before(function() {
serviceClient.sampleResponse = fixture.load('product_response.json')[0];
$.ajaxStub = sinon.stub($, 'ajax').returns(new $.Deferred().resolve(serviceClient.sampleResponse));
});
describe('.getProductName', function() {
it('should return the product name', function() {
var name = serviceClient.getProductName(serviceClient.sampleResponse.id);
$.when($.ajaxStub.returnValue).done(function() {
expect(name).to.equal(serviceClient.sampleResponse.name);
});
});
});
When I step through the call stack it seems to be executing correctly (steps inside of the promise callback in the actual js file, then steps into the test promise callback to assert), however the name variable in the test is still coming back as undefined. Any feedback would be appreciated.
You're trying to return data.name synchronously in serviceClient.getProductName, which can't be done since it depends on an asynchronous ajax request.
You're doing return data.name; inside your callback, which won't get you the expected result: if you'd return something synchronously, that return sentence should be at the scope outside that closure.
To simplify: if there's anything you can return there, it's a Deferred or Promise. It should be something like this:
serviceClient.getProductName = function(id, storeId) {
var deferredName = $.Deferred();
$.when(self._getProduct(id)).done(function(data) {
deferredName.resolve(data.name);
}).fail(function(jqXHR, textStatus, error) {
deferredName.reject(error);
});
return deferredName.promise();
// Or, if needed (I don't think so, it's resolved and rejected here)
// return deferredName;
};
Then, in your test:
before(function() {
serviceClient.sampleResponse = fixture.load('product_response.json')[0];
$.ajaxStub = sinon.stub($, 'ajax').returns(new $.Deferred().resolve(serviceClient.sampleResponse));
});
describe('.getProductName', function() {
it('should return the product name', function() {
serviceClient.getProductName(serviceClient.sampleResponse.id)
.done(function(name) {
expect(name).to.equal(serviceClient.sampleResponse.name);
});
});
});
Leaving code aside, the conceptual mistake is that you can return name synchronously from getProductName, when that function gets the product asyncrhonously (it won't be able to access its name until the deferred is resolved).
Note: you could implement getProductName using then, that returns a Promise (which is a subset of Deferred, but you can usually get away with it and code looks even clearer):
serviceClient.getProductName = function(id, storeId) {
return $.when(self._getProduct(id)).then(function(data) {
return data.name;
});
};
To remove that, also unnecessary $.when(...), you could return a Promise from _getProduct as well (if you've got a deferred, getting a Promise for it is as simple as calling deferred.promise()).
Related
I am trying to filter/match a list of returned IDs to a list of JSON data records, but I am struggling with (I believe) my promises and method chaining.
I can get the functions to work, except for when I add step 3 below. Then it resolves without the matching data (the function does carry on and eventually return the correct matching data, but by that time my method has already completed).
This is how it is supposed to work:
(getCompanyBrandProfileIDs) First my method gets a brandProfileID linked to the current user.
(getBrandProfiles) Then it takes the brandProfileID and get all brandProfiles linked to the specific brandProfile.
(getKeywordProfiles) Then it SHOULD take the returned brandProfiles, and get the matching keywordProfile for each brandProfile. It is an array of objects containing a brand_profile_id and and id.
This is my main method:
this.getCompanyBrandProfileIDs = function () {
var brandProfileIDsToReturn = $q.defer();
GetUserAccessService.returnBrandProfileID().then(function (brandProfileID) {
console.log(brandProfileID);
getBrandProfiles(brandProfileID).then(function (brandProfiles) {
console.log(JSON.stringify(brandProfiles));
var keywordProfilesArray = [];
getKeywordProfiles(brandProfiles).then(function (keywordProfiles) {
keywordProfilesArray = keywordProfiles;
console.log(JSON.stringify(keywordProfilesArray));
//brandProfileIDsToReturn.resolve(keywordProfilesArray);
});
brandProfileIDsToReturn.resolve(keywordProfilesArray);
});
});
return brandProfileIDsToReturn.promise;
};
This is the getBrandProfiles method:
function getBrandProfiles(brandProfileID) {
var getBrandProfilesLinkedToCompany = $q.defer();
pullSocialMediaData('keyword_profile_brand_profiles.json?brand_profile_id=' + brandProfileID).then(function (brandProfiles) {
var brandProfilesArray = [];
brandProfiles.forEach(function (profile) {
brandProfilesArray.push({ id: profile.id, name: profile.name });
});
getBrandProfilesLinkedToCompany.resolve(brandProfilesArray);
});
return getBrandProfilesLinkedToCompany.promise;
}
This is the getKeywordProfiles method:
function getKeywordProfiles(brandProfiles) {
var keywordProfilesToReturn = $q.defer();
var brandProfilesArray = brandProfiles;
var array = [];
brandProfilesArray.forEach(function (profile) {
findKeywordProfile(profile.id).then(function (keywordID) {
array.push(keywordID);
});
keywordProfilesToReturn.resolve(array);
})
return keywordProfilesToReturn.promise;
}
This is a helper method for getKeywordProfiles:
function findKeywordProfile(brandProfileID) {
var keywordProfileID = $q.defer();
pullSocialMediaData('list_keyword_profiles.json').then(function (data) {
var keywordProfileInstance = data.filter(function (keyword) {
return keyword.brand_profile_id === brandProfileID;
});
keywordProfileID.resolve(keywordProfileInstance[0].id);
});
return keywordProfileID.promise;
}
I would appreciate your assistance!
You are resolving the brandProfileIDsToReturn too soon. In this code: when you resolve the promise the then callback will not have been called, so keywordProfilesArray is guaranteed to be empty.
this.getCompanyBrandProfileIDs = function () {
var brandProfileIDsToReturn = $q.defer();
GetUserAccessService.returnBrandProfileID().then(function (brandProfileID) {
console.log(brandProfileID);
getBrandProfiles(brandProfileID).then(function (brandProfiles) {
console.log(JSON.stringify(brandProfiles));
var keywordProfilesArray = [];
getKeywordProfiles(brandProfiles).then(function (keywordProfiles) {
keywordProfilesArray = keywordProfiles;
console.log(JSON.stringify(keywordProfilesArray));
//brandProfileIDsToReturn.resolve(keywordProfilesArray);
});
brandProfileIDsToReturn.resolve(keywordProfilesArray);
});
});
return brandProfileIDsToReturn.promise;
};
Simply moving the resolve() call inside the then callback should fix it and in fact you have that line commented out, so uncomment it and remove the other resolve:
this.getCompanyBrandProfileIDs = function () {
var brandProfileIDsToReturn = $q.defer();
GetUserAccessService.returnBrandProfileID().then(function (brandProfileID) {
console.log(brandProfileID);
getBrandProfiles(brandProfileID).then(function (brandProfiles) {
console.log(JSON.stringify(brandProfiles));
var keywordProfilesArray = [];
getKeywordProfiles(brandProfiles).then(function (keywordProfiles) {
keywordProfilesArray = keywordProfiles;
console.log(JSON.stringify(keywordProfilesArray));
brandProfileIDsToReturn.resolve(keywordProfilesArray);
});
});
});
return brandProfileIDsToReturn.promise;
};
However you can probably simplify the code a lot if you stop using $q.defer(). Your functions already return promises so just return the promises they use and stop trying to create additional promises. I think this is equivalent to the previous code except it returns the promises directly, and I removed the log messages, and that means the getKeywordProfiles call simplifies down to a callback that just calls the function so you can pass the function directly:
this.getCompanyBrandProfileIDs = function () {
return GetUserAccessService.returnBrandProfileID().then(function (brandProfileID) {
return getBrandProfiles(brandProfileID).then(getKeywordProfiles);
});
});
};
and then you can simplify it further by extracting the inner .then:
this.getCompanyBrandProfileIDs = function () {
return GetUserAccessService.returnBrandProfileID()
.then(getBrandProfiles)
.then(getKeywordProfiles);
};
The getKeywordProfiles() function also needs to avoid resolving its promise until all of the findKeywordProfile() calls have resolved. Return a promise for the array of promises and when they resolve the promise will complete to an array of values:
function getKeywordProfiles(brandProfilesArray) {
var array = [];
brandProfilesArray.forEach(function (profile) {
array.push(findKeywordProfile(profile.id));
})
return $q.all(array);
}
To clarify my comments about $q, there are some cases where you need to create a promise from scratch using it, but they're fairly uncommon. Anything that happens asynchronously in Angular already returns a promise, and the great thing about promises is that they chain together, so when you have one promise calling .then() or .catch() will return a new one. Also the .then() callback can either return a value which resolves the new promise, or can return a promise which will only resolve the new promise when it, itself resolves. So just keep chaining the .then() calls together and each will wait for the previous one to complete.
$q is still useful though: $q.all() if you want to wait for a bunch of promises to all resolve, $q.race() if you have a bunch of promises and only one needs to resolve, $q.resolve(value) can also be useful as sometimes you just want a promise that will resolve immediately so you can hang a chain of other async functions off it.
Also it is safe to keep a promise around even long after it has resolved and you can still call .then() on it: this is useful if you have asynchronous initialisation code and events that may or may not be triggered before the initialisation has completed. No need to do if(isInitialised) when you can just do initPromise.then(...)
In getKeywordProfiles function you need resolve it when array loop finished.
function getKeywordProfiles(brandProfiles) {
var keywordProfilesToReturn = $q.defer();
var brandProfilesArray = brandProfiles;
var array = [];
brandProfilesArray.forEach(function (profile) {
findKeywordProfile(profile.id).then(function (keywordID) {
array.push(keywordID);
});
//--
//keywordProfilesToReturn.resolve(array);
})
//++
keywordProfilesToReturn.resolve(array);
return keywordProfilesToReturn.promise;
}
Info: I think you need to create an profileIdArray push all brandProfileID and send to your findKeywordProfile function. It will be more useful.
I have the following code that causes the two call to Webtrends to be cancelled (ie these two calls did not give a http 200 but a cancelled message in the network tab of the browser) when I call it
mercury.Tracking.logUsage("export", GetSelectedExportType(form));
mercury.Tracking.logUsage('exportchart', mercury.ChartContainer.currentChartUri(), path);
form[0].submit();
I rewrote this in this way to avoid this issue, as it seemed to me that the reason why the calls to Webtrends were being cancelled was because the form submit was making that happen so before calling submit on the form I wait two seconds.
mercury.Tracking.logUsage("export", GetSelectedExportType(form));
mercury.Tracking.logUsage('exportchart', mercury.ChartContainer.currentChartUri(), path);
var submit = function () {
setTimeout(function() {
form[0].submit();
}, 2000);
};
submit();
Question is, is there a better way, using promises or callbacks or whatever to do this?
The logUsage code is
(function ($, window) {
function Tracking() {
}
Tracking.prototype.chartTitle = function () {
return $('#chartNameInfo').text();
};
Tracking.prototype.hostName = function () {
return $('#trackingVars').data('host-name');
};
Tracking.prototype.page = function () {
return $('#trackingVars').data('page');
};
Tracking.prototype.currentUser = function () {
return window.config.userId;
};
Tracking.prototype.logUsage = function (action, resourceUri, actionTargetUri, additionalTags) {
// action: action performed - e.g. create, delete, export
// resourceUri: URI of API resource *on* which action is being performed (required), e.g. /users/current/annotations/{annotation-id}
// actionTargetUri: URI of API resource *to* which action is being performed (optional), e.g. /charts/{chart-id}
if (action.indexOf("DCSext.") < 0) {
action = "DCSext." + action;
}
var jsonString = '{"' + action + '"' + ':"1"}';
var jsonObj = JSON.parse(jsonString);
if (additionalTags == null) {
additionalTags = jsonObj;
}
else {
additionalTags = $.extend({}, additionalTags, jsonObj); //Append two JSON objects
}
var trackingargs = $.extend({
'DCSext.resource-uri': resourceUri,
'DCSext.action-target-uri': actionTargetUri,
'WT.ti': this.chartTitle(),
'DCSext.dcssip': this.hostName(),
'DCSext.em-user-id': this.currentUser(),
dsci_uri: this.page()
}, additionalTags);
try {
WebTrends.multiTrack({ args: trackingargs });
} catch (e) {
console.log(e);
}
};
window.Tracking = new Tracking();
$(function() {
$('body').on('click', 'a[data-tracking-action]', function() {
window.Tracking.logUsage($(this).data('tracking-action'), $(this).data('tracking-resource'));
});
$(document).on('attempted-access-to-restricted-resource', function(event, href) {
window.Tracking.logUsage('unauthorisedResourceAccessUpsell', href.url);
});
});
})(jQuery, window);
With the extra information provided, I think I can now answer your question.
From WebTrends doc, you can add a finish callback to your WebTrends.MultiTrack call.
What you could do:
Tracking.prototype.logUsage = function (action, resourceUri, actionTargetUri, additionalTags) {
...
var finished = $.Deferred();
...
try {
WebTrends.multiTrack({ args: trackingargs, finish: function(){finished.resolve();}});
}
...
return finished;
}
and then in your code:
$.when(mercury.Tracking.logUsage("export", GetSelectedExportType(form)),
mercury.Tracking.logUsage('exportchart', mercury.ChartContainer.currentChartUri(), path))
.done(function(){
form[0].submit();
});
I have not tested this, but I think it should work. Hope it helps.
Explanations:
jQuery.when()
Description: Provides a way to execute callback functions based on one
or more objects, usually Deferred objects that represent asynchronous
events.
Basically, jQuery.when() will take one or more deferreds (which build promises) or promises and will return one promise that fulfills when they all fulfill. From there, we can choose to add handlers using th e .done() or .then() method to our promise, which will be called once or promise is fulfilled . (A promise represents the result of an asynchronous operation).
So, in the code above, I created a new deferred object in your logUsage method, and that method returns the deferred, so you can pass those deferreds to jQuery.when method and when they will be fulfilled (this is why I added the finish callback in your WebTrends.Multitrack call), the handler passed to deferred.done() will be executed.
I hope this is not too confusing, I'm not sure I'm explaining it correctly.
Not trying to steal Antoine's rep. His answer is essentially fine, but the ... sections can be fleshed out far more efficiently than in the question, plus a few other points for consideration.
Tracking.prototype.logUsage = function (action, resourceUri, actionTargetUri, additionalTags) {
// action: action performed - e.g. create, delete, export
// resourceUri: URI of API resource *on* which action is being performed (required), e.g. /users/current/annotations/{annotation-id}
// actionTargetUri: URI of API resource *to* which action is being performed (optional), e.g. /charts/{chart-id}
try {
// you might as well wrap all the preamble in the try{}, just in case it it error-prone
if (action.indexOf("DCSext.") < 0) {
action = "DCSext." + action;
}
//trackingargs can be defined efficiently as follows, avoiding the need for the variable `jsonObj` and the ugly JSON.parse().
var trackingargs = $.extend({
'DCSext.resource-uri': resourceUri,
'DCSext.action-target-uri': actionTargetUri,
'WT.ti': this.chartTitle(),
'DCSext.dcssip': this.hostName(),
'DCSext.em-user-id': this.currentUser(),
'dsci_uri': this.page()
}, additionalTags || {}); // `additionalTags || {}` caters for missing or null additionalTags
trackingargs[action] = 1;//associative syntax gets around the limitation of object literals (and avoids the need for JSON.parse()!!!).
//to keep things tidy, return $.Deferred(fn).promise()
return $.Deferred(function(dfrd) {
WebTrends.multiTrack({
args: trackingargs,
finish: dfrd.resolve //no need for another function wrapper. `$.Deferred().resolve` and `$.Deferred().reject` are "detachable"
});
}).promise();//be sure to return a promise, not the entire Deferred.
} catch (e) {
console.log(e);
//Now, you should really ensure that a rejected promise is always returned.
return $.Deferred.reject(e).promise();//Surrogate re-throw.
}
};
see comments in code
As Tracking.prototype.logUsage can now return a rejected promise, and as you probably don't want .logUsage() failure to inhibit your form submission, you probably want to convert rejected promises to fulfilled.
$.when(
mercury.Tracking.logUsage("export", GetSelectedExportType(form)).then(null, function() {
return $.when();//resolved promise
}),
mercury.Tracking.logUsage('exportchart', mercury.ChartContainer.currentChartUri(), path).then(null, function() {
return $.when();//resolved promise
})
).done(function() {
form[0].submit();
});
It may seem to be an unnecessary complication to return a rejected promise then convert to success, however :
it is good practice to report asycnhronous failure in the form of a rejected promise, not simply log the error and return undefined.
window.Tracking.logUsage() may be called elsewhere in your code, where it is necessary to handle an error as an error.
The scope variable $scope.quizArray appears as undefined when accessed from the controller. The block of code below is definitely read as I have previously put tests in.
app.factory('getQuizService', function($http){
return {
getQuiz:function(videoId,scope){
var $promise=$http.post("http://localhost/PHP/getQuiz.php", {'videoId': videoId});
$promise.then(function(msg){
scope.quizArray = "TEST";
});
}
}
});
Controller code - The service is called by:
function getQuizList(){
getQuizService.getQuiz(videoIdService.getId(),$scope);
alert($scope.quizArray);
}
however the alert produces the result 'undefined' instead of 'TEST'
Can anyone see where I've gone wrong? Any help would be appreciated. Thanks
Is there a reason you're doing it like this?
Take a look at the approach(es) below:
Refer to: http://docs.angularjs.org/api/ng/service/$q for more information about how the promises work.
For example, the reason why the approach that lib3d has pointed out works:
then(successCallback, errorCallback, notifyCallback) – regardless of when the promise was or will be resolved or rejected, then calls one of the success or error callbacks asynchronously as soon as the result is available. The callbacks are called with a single argument: the result or rejection reason. Additionally, the notify callback may be called zero or more times to provide a progress indication, before the promise is resolved or rejected.
This method returns a new promise which is resolved or rejected via the return value of the successCallback, errorCallback. It also notifies via the return value of the notifyCallback method. The promise can not be resolved or rejected from the notifyCallback method.
Code:
app.factory('getQuizService', function($http){
return {
getQuiz:function(videoId,scope){
return $http.post("http://localhost/PHP/getQuiz.php", {'videoId': videoId});
//If you want to handle the promise here, you can include the $q service in your controller and do this:
var deferred = $q.defer();
$http.post("...").then(function(data) {
//Special treatment here
deferred.resolve(data)
}, function(error) {
deferred.reject(error)
})
return deferred.promise
//As indicated by lib3d, you can directly do this as well:
return $http.post("...").then(function(data) {
//treatment
return data
}, function(error) {
//error handling
});
}
}).controller("Ctrl", function($scope, getQuizService) {
getQuizService.getQuiz(1).then(function(data) {
$scope.quizArray = data;
//Code when everything goes ok,
}, function(error) {
//Code when everything goes false;
})
})
How do I delay execution of a function until after all of my $resources have resolved? My goal here is to be able to parse though the log array after all $resources have resolved and push a single success notification to the UI instead of pushing one notification per each success.
I've based my code below off of this question angular -- accessing data of multiple http calls - how to resolve the promises. I realize that $scope.promises is empty because item.$save() doesn't return anything but I hope you can see that I'm trying to push the unresolved promise to the promises array.
$scope.save = function () {
$scope.promises = [];
$scope.log = [];
angular.forEach($scope.menuItems, function(item) {
$scope.promises.push(item.$save(function(menu){
debugger; // execution gets here 2nd
console.debug("success");
$scope.log.push(msg: 'success');
}));
}, this);
$q.all($scope.promises).then(function() {
debugger; // execution gets here 1st
console.debug("all promises resolved");
});
};
Since $save does not return a promise, you will need an intermediate one:
angular.forEach($scope.menuItems, function(item) {
var d = $q.defer(); // <--- the intermediate promise
item.$save(
function(menu){
debugger;
console.debug("success");
$scope.log.push(msg: 'success');
d.resolve(menu); // <--- resolving it, optionally with the value
},
function(error){
d.reject(error); // <--- rejecting it on error
}
);
$scope.promises.push(d.promise);
}, this);
By the way, do not forget to throw away the array of promises, or you will keep garbage:
$q.all($scope.promises).then(...).always(function() {
$scope.promises = null;
});
And, if $scope.promises is NOT exposed to the view, it does not need to be in the scope; it can be just a var.
I want to use $q.when() to wrap some non-promise callbacks. But, I can't figure out how to resolve the promise from within the callback. What do I do inside the anonymous function to force $q.when() to resolve with my reason?
promises = $q.when(
notAPromise(
// this resolves the promise, but does not pass the return value vvv
function success(res) { return "Special reason"; },
function failure(res) { return $q.reject('failure'); }
)
);
promises.then(
// I want success == "Special reason" from ^^^
function(success){ console.log("Success: " + success); },
function(failure){ console.log("I can reject easily enough"); }
);
The functionality I want to duplicate is this:
promises = function(){
var deferred = $q.defer();
notAPromise(
function success(res) { deferred.resolve("Special reason"); },
function failure(res) { deferred.reject('failure'); }
);
return deferred.promise;
};
promises.then(
// success == "Special reason"
function(success){ console.log("Success: " + success); },
function(failure){ console.log("I can reject easily enough"); }
);
This is good, but when() looks so nice. I just can't pass the resolve message to then().
UPDATE
There are better, more robust ways to do this. $q throws exceptions synchronously, and as #Benjamin points out, the major promise libs are moving toward using full Promises in place of Deferreds.
That said, this question is looking for a way to do this using $q's when() function. Objectively superior techniques are of course welcome but don't answer this specific question.
The core problem
You're basically trying to convert an existing callback API to promises. In Angular $q.when is used for promise aggregation, and for thenable assimilation (that is, working with another promise library). Fear not, as what you want is perfectly doable without the cruft of a manual deferred each time.
Deferred objects, and the promise constructor
Sadly, with Angular 1.x you're stuck with the outdated deferred interface, that not only like you said is ugly, it's also unsafe (it's risky and throws synchronously).
What you'd like is called the promise constructor, it's what all implementations (Bluebird, Q, When, RSVP, native promises, etc) are switching to since it's nicer and safer.
Here is how your method would look with native promises:
var promise = new Promise(function(resolve,reject){
notAPromise(
function success(res) { resolve("Special reason") },
function failure(res) { reject(new Error('failure')); } // Always reject
) // with errors!
);
You can replicate this functionality in $q of course:
function resolver(handler){
try {
var d = $q.defer();
handler(function(v){ d.resolve(v); }, function(r){ d.reject(r); });
return d.promise;
} catch (e) {
return $q.reject(e);
// $exceptionHandler call might be useful here, since it's a throw
}
}
Which would let you do:
var promise = resolver(function(resolve,reject){
notAPromise(function success(res){ resolve("Special reason"),
function failure(res){ reject(new Error("failure")); })
});
promise.then(function(){
});
An automatic promisification helper
Of course, it's equally easy to write an automatic promisification method for your specific case. If you work with a lot of APIs with the callback convention fn(onSuccess, onError) you can do:
function promisify(fn){
return function promisified(){
var args = Array(arguments.length + 2);
for(var i = 0; i < arguments.length; i++){
args.push(arguments[i]);
}
var d = $q.defer();
args.push(function(r){ d.resolve(r); });
args.push(function(r){ d.reject(r); });
try{
fn.call(this, args); // call with the arguments
} catch (e){ // promise returning functions must NEVER sync throw
return $q.reject(e);
// $exceptionHandler call might be useful here, since it's a throw
}
return d.promise; // return a promise on the API.
};
}
This would let you do:
var aPromise = promisify(notAPromise);
var promise = aPromise.then(function(val){
// access res here
return "special reason";
}).catch(function(e){
// access rejection value here
return $q.reject(new Error("failure"));
});
Which is even neater
Ok here's my interpretation of what I think you want.
I am assuming you want to integrate non-promise callbacks with a deferred/promise?
The following example uses the wrapCallback function to wrap two non-promise callbacks, successCallback and errCallback. The non-promise callbacks each return a value, and this value will be used to either resolve or reject the deferred.
I use a random number to determine if the deferred should be resolved or rejected, and it is resolved or rejected with the return value from the non-promise callbacks.
Non angular code:
function printArgs() {
console.log.apply(console, arguments);
}
var printSuccess = printArgs.bind(null, "success");
var printFail = printArgs.bind(null, "fail");
function successCallback() {
console.log("success", this);
return "success-result";
}
function errCallback() {
console.log("err", this);
return "err-result";
}
function wrapCallback(dfd, type, callback, ctx) {
return function () {
var result = callback.apply(ctx || this, arguments);
dfd[type](result);
};
}
Angular code:
var myApp = angular.module('myApp', []);
function MyCtrl($scope, $q) {
var dfd = $q.defer();
var wrappedSuccess = wrapCallback(dfd, "resolve", successCallback);
var wrappedErr = wrapCallback(dfd, "reject", errCallback);
var rnd = Math.random();
var success = (rnd > 0.5);
success ? wrappedSuccess() : wrappedErr();
console.log(rnd, "calling " + (success ? "success" : "err") + " callback");
dfd.promise.then(printSuccess, printFail);
}
Example output where the random number is less than 0.5, and so the deferred was rejected.
err Window /fiddlegrimbo/m2sgu/18/show/
0.11447505658499701 calling err callback
fail err-result