How can I synchronize a callback code? - javascript

I have to get description in the tmp variable and I don't know how to synchronize this code, can someone help me ?
We want to render the contact user first_name in the calendar i.e. attach title to user.first_name. So We are grabbing all the events from the server, however for each event there are bookings and bookings contain the user id to grab the user data from contact_users. Then we need to construct the object and push it to array that has all the events namely tmp. The callback is called at the end to render the events in the calendar.
Event.query({
businessId: $stateParams.businessId
})
.$promise.then(function(events) {
events.forEach(function(event) {
var tmpData = {};
var description = '';
$http.get('/businesses/'+event.business_id+'/events/'+event.id+'/bookings')
.then(function(bookings) {
if(bookings.data) {
$http.get('/businesses/'+event.business_id+'/contact_users/'+bookings.data[0].people_id)
.then(function(user) {
description = user.data.first_name;
});
}
});
tmpData = {
eventId: event.id,
title: description,
start: event.starts_at,
end: event.ends_at,
business_id: event.business_id,
employment_id: event.employment_id,
professional_id: event.professional_id,
service_id: event.service_id,
};
tmp.push(tmpData);
});
return tmp;
}).then(function(result) {
callback(tmp);
});
The callback is related to fullcalendar callback event fired in the events method.

There are two key concepts when dealing with Promise callbacks:
Returning a value from a Promise success callback causes causes the next promise to be resolved with this value.
$q.when().then(function () {
return 3;
}).then(function (result) {
// result === 3
});
Returning another Promise from a Promise success callback effectively replaces the existing Promise.
$q.when().then(function () {
return $timeout(function () { return 3 }, 1000);
}).then(function (result) {
// called 1000ms later
// result === 3
});
Additionally, there is a construct $q.all(promises) which takes an array of promises, and returns a new promise that is resolved when promises are all resolved (or when one of them is rejected).
I don't have access to your backend, so I could not test this, but something like this should work for you:
Event.query({ businessId: $stateParams.businessId }).$promise
.then(function (events) {
// get array of $HttpPromise objects
var promises = events.map(function (event) {
return $http.get('/businesses/' + event.business_id + '/events/' + event.id + '/bookings')
.then(function (response) {
var bookings = response.data;
// "transformed" event object
var evt = {
eventId: event.id,
title: '',
start: event.starts_at,
end: event.ends_at,
business_id: event.business_id,
employment_id: event.employment_id,
professional_id: event.professional_id,
service_id: event.service_id
};
// each promised is replaced either with a new $HttpPromise...
if (bookings) {
return $http.get('/businesses/' + event.business_id + '/contact_users/' + bookings[0].people_id)
.then(function (response) {
var user = response.data;
evt.title = user.first_name;
return evt;
});
}
// ...or with an immediately resolved event
return evt;
})
});
// wait for all promises to be resolved
return $q.all(promises);
}).then(function (results) {
// results is an array of transformed events
callback(results);
});
Side note: another option is to not wait for the inner $http promise to resolve, and simply return the "incomplete" evt object.
// launch a promise which updates evt when resolved
if (bookings) {
$http.get('/businesses/' + event.business_id + '/contact_users/' + bookings[0].people_id)
.then(function (response) {
var user = response.data;
// update evt reference
evt.title = user.first_name;
});
}
// immediately resolve with "incomplete" evt
return evt;
Angular triggers a digest every time a promise is resolved. Depending on how you set up your templates/rendering, this could have the effect of first rendering all events with an empty title, and then re-rendering with first_names when they become available. Note that this this requires that you maintain evt references everywhere between your callback and your templates.

Related

Promise .then() Not Waiting For Resolve() Of the Previous Promise In A Promise Chain

I am very new to the concept of Promises but I read that this is an efficient way of making functions execute one after another by doing something called Promise chaining.
The //RUN ON CLCK: CREATE TABLES code below basically makes two AJAX calls, "Create Database Tables" and "Check Database Tables", so that when I press a button on the webpage, Database Tables will be created on the backend, and then their existence will be checked.
But it doesn't run as intended. In many cases, as obseved from the console log, the second function runs (or) finishes first. In any case, that shouldn't happen in a chain.
It looks like the 2nd function is not waiting for a resolve from the 1st function.
Please note that my functions have arguments so I can't avoid calling them without parentheses inside the then() as some other articles recommend.
$(document).ready(function() {
/*PAGE VARS*/
var mainAdmStgChkTblSrvltMsg_elm = document.getElementById('mainAdmStgChkTblSrvltMsg');
var mainAdmStgCrDelTblSrvltMsg_elm = document.getElementById('mainAdmStgCrDelTblSrvltMsg');
var mainAdmStgCrTblBtn_elm = document.getElementById('mainAdmStgCrTblBtn');
/*FN DEF: CHECK TABLES*/
var chkTbl = function(tblNm) {
return new Promise(function(resolve, reject) {
$.ajax({
type: 'GET',
url: '../../../../../../app/TblSrvlt',
data: {
getType: 'chkTbl',
tblNm: tblNm
},
success: function(data) {
var srvltMsg = data.srvltMsg;
var srvltSuccess = data.srvltSuccess;
mainAdmStgChkTblSrvltMsg_elm.textContent = srvltMsg;
if (srvltSuccess === true) {
mainAdmStgChkTblSrvltMsg_elm.setAttribute('class', 'text-success');
} else {
mainAdmStgChkTblSrvltMsg_elm.setAttribute('class', 'text-danger');
}
/*RETURN RESOLVE FOR PROMISE CHAIN*/
console.log("chkTbl");
resolve();
}
});
});
};
/*FN DEF: CREATE TABLES*/
var crTbl = function(tblNm) {
return new Promise(function(resolve, reject) {
$.ajax({
type: 'POST',
url: '../../../../../../app/TblSrvlt',
data: {
postType: 'crTbl',
tblNm: tblNm
},
success: function(data) {
var srvltMsg = data.srvltMsg;
var srvltSuccess = data.srvltSuccess;
mainAdmStgCrDelTblSrvltMsg_elm.textContent = srvltMsg;
if (srvltSuccess === true) {
mainAdmStgCrDelTblSrvltMsg_elm.setAttribute('class', 'text-success');
} else {
mainAdmStgCrDelTblSrvltMsg_elm.setAttribute('class', 'text-danger');
}
/*RETURN RESOLVE FOR PROMISE CHAIN*/
console.log("crTbl");
resolve();
}
});
});
};
/*RUN ON CLCK: CREATE TABLES*/
$(document).on('click', '#' + mainAdmStgCrTblBtn_elm.id, function() {
Promise.resolve()
.then(crTbl("chairs"))
.then(chkTbl("chairs"))
.catch();
});
});
You have to pass a function reference to .then() so it can call your function LATER. Instead, you are calling the functions immediately and passing their return value to .then() which is why they are getting called immediately and not waiting for the prior promise to resolve.
Change this:
/*RUN ON CLCK: CREATE TABLES*/
$(document).on('click', '#' + mainAdmStgCrTblBtn_elm.id, function() {
Promise.resolve()
.then(crTbl("chairs"))
.then(chkTbl("chairs"))
.catch();
});
to this:
/*RUN ON CLCK: CREATE TABLES*/
$(document).on('click', '#' + mainAdmStgCrTblBtn_elm.id, function() {
Promise.resolve()
.then(function() {return crTbl("chairs")})
.then(function() {return chkTbl("chairs")})
.catch(function(err) { console.log(err)});
});
And, you don't actually need the Promise.resolve() at the start of the chain:
/*RUN ON CLCK: CREATE TABLES*/
$(document).on('click', '#' + mainAdmStgCrTblBtn_elm.id, function() {
crTbl("chairs")
.then(function() {return chkTbl("chairs")})
.catch(function(err) { console.log(err)});
});

Finish loop of ajax calls array instead of breaking on error from one of calls

I'm trying to make loop of ajax calls from an array and saving each data result that I'll print when all calls are successfully received.
The problem is that if there is any ajax call returning error, the whole process is aborted and the callback is not executed. (Try using listWithErrorResult)
How to push something on error without breaking the loop?
JSFIDDLE
var list = ['UQ13nr6urIo', 'hxa_Z0eZ83E', 'ulHB2mNlovg'];
var listWithErrorResult = ['UQ13nr6urIo', 'hxa_Z0eZ83E', 'IWEHeIdBkc4'];
var callback = function() {
console.log("done");
console.log(tracks);
};
var requests = [], tracks = [];
for(i = 0; i < list.length; i++) {
requests.push($.ajax({
url: 'http://www.youtubeinmp3.com/fetch/?format=JSON&video=https://www.youtube.com/watch?v='+list[i],
dataType: "json",
success: function(data) {
console.log('suc');
tracks.push(data);
},error: function() {
console.log('err');
tracks.push('err');
}
}));
}
$.when.apply(undefined, requests).then(function(results){callback()});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You can just attach a .then() handle to your ajax call and make sure every ajax call resolves rather than rejects. This will get you an array of results at the end that is guaranteed to include all your ajax calls.:
var callback = function(/* tracks are passed here as arguments */) {
console.log("done");
var tracks = Array.prototype.slice.call(arguments);
console.log(tracks);
};
var requests = [];
for(var i = 0; i < list.length; i++) {
requests.push($.ajax({
url: 'http://www.youtubeinmp3.com/fetch/?format=JSON&video=https://www.youtube.com/watch?v='+list[i],
dataType: "json",
}).then(function(data) {
return data;
}, function(err) {
// convert failure into success so $.when() doesn't stop
return $.Deferred().resolve('err');
}));
}
$.when.apply($, requests).then(callback);
You can also use a version of $.settle() that I wrote that lets all promises finish and gives you all results, even if some promises reject.
Instead of $.when() (which stops on the first rejected promise), you can use this new function $.settle() which returns you the results of all the promises. It returns an array of PromiseInspection objects which allow you to query the resolved value or rejected reason for each promise.
You use $.settle() just like $.when() except it returns all results in PromiseInspection objects and it always resolves (never rejects). Because of jQuery's non-standard means of passing multiple arguments to .then() handlers (particular Ajax calls), if .settle() detects multiple arguments, it copies them into an array so the PromiseInspection.value returned from a successful ajax call is an array [data, textStatus, jqXHR]. This is the worst part of jQuery's non-standard promises. Presumably this is fixed/changed in jQuery 3.x which is supposed to be standard's compliant. This code will work with either since it auto-detects if the .then() handler is sent more than one argument and converts them into a single array argument.
// ES6 version of settle
jQuery.settle = function(promises) {
function PromiseInspection(fulfilled, val) {
return {
isFulfilled: function() {
return fulfilled;
}, isRejected: function() {
return !fulfilled;
}, isPending: function() {
// PromiseInspection objects created here are never pending
return false;
}, value: function() {
if (!fulfilled) {
throw new Error("Can't call .value() on a promise that is not fulfilled");
}
return val;
}, reason: function() {
if (fulfilled) {
throw new Error("Can't call .reason() on a promise that is fulfilled");
}
return val;
}
};
}
return $.when.apply($, promises.map(function(p) {
return p.then(function(val) {
if (arguments.length > 1) {
val = Array.prototype.slice.call(arguments);
}
return new PromiseInspection(true, val);
}, function(err) {
if (arguments.length > 1) {
err = Array.prototype.slice.call(arguments);
}
return new PromiseInspection(false, err);
});
}));
}
You have to add error callback to $.when.apply then too as its an error condition when one of the call fails.
$.when.apply(undefined, requests).then(
function(results){callback()},
function(results){callback()}
);
Working jsBin here

Why does my Promise Chain not work in this nested way?

I want to chain 4 functions in a Promise chain like so:
function1 -> function2 -> function3 -> function4
My Promise chain
if ($location.$$url !== "/dashboard") {
vm.customURL = true;
// (1) Set root vars & Rebuild tickerTagsContainer:
var promise = TagFactory.buildUrlObject($location.$$url).then(function() {
console.log('TagFactory.buildUrlObject PROMISE returned');
}).then(function() {
console.log('(2) Re-display tags in viewHeader');
// (2) Re-display tags in viewHeader:
viewHeader = ScopeFactory.getScope('viewHeader');
viewHeader.vh.displayViewHeaderTags().then(function() {
console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
});
}).then(function() {
// (3) Reselect timeSpan:
console.log('(3) Reselect timeSpan');
viewHeader.vh.toggleTimeSpan(vm.timeSpan);
// (4) Refresh URL:
console.log('(4) Refresh URL');
ViewFactory.remakeViewObject($location.$$url);
});
}
The resulting console.logs:
^ Note I never see this log:
viewHeader.vh.displayViewHeaderTags().then(function() {
console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
});
Ideally I want to place my (3) function inside that, then chain my (4) like so:
viewHeader.vh.displayViewHeaderTags().then(function() {
console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
console.log('(3) Reselect timeSpan');
viewHeader.vh.toggleTimeSpan(vm.timeSpan).then(function() {
console.log('(4) Refresh URL');
ViewFactory.remakeViewObject($location.$$url);
});
});
However I never see the console.log from the .then function for displayViewHeaderTags
Here is what my displayViewHeaderTags looks like:
function displayViewHeaderTags() {
vm.viewTickerTags = [];
vm.viewTickerTags = TagFactory.retrieveTickerTags('all');
var deferred = $q.defer();
var tikObjs = vm.viewTickerTags.map(function(el) { return el.ticker; });
var tagObjs = vm.viewTickerTags.map(function(el) { return el.tags; });
var tags = _.flatten(tagObjs);
// forEach loops up to 3 times:
tags.forEach(function(tag, i) {
vm.viewTags = [];
ApiFactory.getTagDataSilm(tag.term_id).then(function(data) {
vm.viewTags.push(data.data.ticker_tag);
if (i === tags.length) {
deferred.resolve();
}
});
});
return deferred.promise;
}
Inside my displayViewHeaderTags function I hit a loop which will run up to 3 times, after it's done getting data, it will fill up and array then calls deffered.resolve. then returns it return deferred.promise;
So why do I never see this log? console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
Your i is never the same as the length, because the i variable starts at zero (array indexes start at zero). Which means the if you have an array with length = 2, your i values will be 0 and 1 respectively. It will never equal to zero. Basically, you would want the condition to be:
vm.viewTags.push(data.data.ticker_tag);
if (i + 1 === tags.length) {
deferred.resolve();
}
Anyway, using defer() is a code smell.
A more elegant way of doing it would be using $q.all
var allPromises = [];
var promise;
tags.forEach(function(tag) {
vm.viewTags = [];
promise = ApiFactory.getTagDataSilm(tag.term_id).then(function(data) {
vm.viewTags.push(data.data.ticker_tag);
});
// Create an array of promises, one promise for each request
allPromises.push( promise );
});
// Return a new promise that will only be resolved
// when all the promises of the array `allPromises` are resolved,
// or is rejected when one of them is.
return $q.all( allPromises );
Your chain is not really doing anything since you're not returning a promise from any of those anonymous functions. You're not seeing that log probably because ApiFactory.getTagDataSilm is failing or never resolving. Try adding an error handler into your flow.
if ($location.$$url !== "/dashboard") {
vm.customURL = true;
// (1) Set root vars & Rebuild tickerTagsContainer:
var promise = TagFactory.buildUrlObject($location.$$url).then(function() {
console.log('TagFactory.buildUrlObject PROMISE returned');
}).then(function() {
console.log('(2) Re-display tags in viewHeader');
// (2) Re-display tags in viewHeader:
viewHeader = ScopeFactory.getScope('viewHeader');
return viewHeader.vh.displayViewHeaderTags().then(function() {
console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
});
}).then(function() {
// (3) Reselect timeSpan:
console.log('(3) Reselect timeSpan');
return viewHeader.vh.toggleTimeSpan(vm.timeSpan);
}).then(function() {
// (4) Refresh URL:
console.log('(4) Refresh URL');
return ViewFactory.remakeViewObject($location.$$url);
}).catch(function(error) {
console.log('Something failed', error);
});
}
Within displayViewHeaderTags, you can use $q.all, so that rejections are handled for you:
// forEach loops up to 3 times:
vm.viewTags = [];
return $q.all(_.map(tags, function(tag) {
return ApiFactory.getTagDataSilm(tag.term_id).then(function(data) {
vm.viewTags.push(data.data.ticker_tag);
});
}));

JavaScript Promises

Im trying to understand promises, but Im hitting a roadblock, I'm trying to query my Parse database for the last ran date object so that ill know when it was ran last. Then pass that date to the next function who can check my movie database for anything after the last time it was called. (I'm doing this to send out push notifications for new fields manually entered into Parse class) then actually send the push. But I'm not understanding the .then and promises, I'm new to JavaScript so any help would be appreciated!
Here is my code i have now.
Parse.Cloud.job("TestSendNewMoviePush", function(request, response) {
var query = new Parse.Query("MovieStatus");
var lastRunDateQuery = new Parse.Query("LastRun");
var lastRunDate;
var newDate;
var newCount = 0;
var installQuery = new Parse.Query(Parse.Installation);
query.greaterThan("updatedAt", lastRunDate);
query.equalTo("CurrentStatus", "Ready");
query.equalTo("imageStatusName", "Green");
installQuery.equalTo("role", "downloader");
lastRunDateQuery.get("d3WeNEwzIu", {
success: function(lastDateRanObj) {
console.log("Got the object " + lastDateRanObj);
var date = new lastDateRanObj.updatedAt;
lastRunDate = lastDateRanObj.updatedAt;
console.log(lastRunDate);
return lastRunDate;
},
error: function(lastDateRanObj, error) {
console.log("Failed to get object");
}
}).then(
query.count({
success: function(count) {
newCount = count;
return newCount
},
error: function(e) {
}
})).then(
Parse.Push.send({
where: installQuery,
data: {
"alert": newCount + " new movie(s) available!",
"badge": "Increment"
}
}, {
success: function() {
response.success("Success");
},
error: function(e) {
response.error("Error:" + e.code);
}
}));
});
lastRunDateQuery.get() returns a Promise object, which you can chain with a then, which itself is a function taking 2 functions as arguments: one that is called if the promise is resolved, and one that is called if the promise is rejected. You use these instead of the success and error parameters:
lastRunDateQuery.get()
.then(function(data) {
// success!!
}, function(error) {
// error :(
});
The functions you passed as arguments to then can themselves return promises, which you may subsequently chain with a then. In the example below I have omitted the error callbacks:
lastRunDateQuery.get()
.then(function(data) {
// data is the result of lastRunDateQuery.get()
return query.count();
})
.then(function(data) {
// data is the result of query.count()
return someOtherThing.someOtherMethodReturningAPromise();
});
.then(function(data) {
// data is the result of someOtherThing.someOtherMethodReturningAPromise()
});
And so on.
I would recommend having a look at the Promises/A+ spec - it's very instructive.
EDIT:
If the chaining concept is a bit confusing just think of it as a shorthand for the following:
var aPromise = lastRunDateQuery.get();
aPromise.then(
function() {}, // promise was resolved -> call this function
function() {}, // promise was rejected -> call this function
);

Multiple Promise Chains in Single Function

I have some code that will dynamically generate an AJAX request based off a scenario that I'm retrieving via an AJAX request to a server.
The idea is that:
A server provides a "Scenario" for me to generate an AJAX Request.
I generate an AJAX Request based off the Scenario.
I then repeat this process, over and over in a Loop.
I'm doing this with promises here: http://jsfiddle.net/3Lddzp9j/11/
However, I'm trying to edit the code above so I can handle an array of scenarios from the initial AJAX request.
IE:
{
"base": {
"frequency": "5000"
},
"endpoints": [
{
"method": "GET",
"type": "JSON",
"endPoint": "https://api.github.com/users/alvarengarichard",
"queryParams": {
"objectives": "objective1, objective2, objective3"
}
},
{
"method": "GET",
"type": "JSON",
"endPoint": "https://api.github.com/users/dkang",
"queryParams": {
"objectives": "objective1, objective2, objective3"
}
}
]
This seems like it would be straight forward, but the issue seems to be in the "waitForTimeout" function.
I'm unable to figure out how to run multiple promise chains. I have an array of promises in the "deferred" variable, but the chain only continues on the first one--despite being in a for loop.
Could anyone provide insight as to why this is? You can see where this is occuring here: http://jsfiddle.net/3Lddzp9j/10/
The main problems are that :
waitForTimeout isn't passing on all the instructions
even if waitForTimeout was fixed, then callApi isn't written to perform multiple ajax calls.
There's a number of other issues with the code.
you really need some data checking (and associated error handling) to ensure that expected components exist in the data.
mapToInstruction is an unnecessary step - you can map straight from data to ajax options - no need for an intermediate data transform.
waitForTimeout can be greatly simplified to a single promise, resolved by a single timeout.
synchronous functions in a promise chain don't need to return a promise - they can return a result or undefined.
Sticking with jQuery all through, you should end up with something like this :
var App = (function ($) {
// Gets the scenario from the API
// sugar for $.ajax with GET as method - NOTE: this returns a promise
var getScenario = function () {
console.log('Getting scenario ...');
return $.get('http://demo3858327.mockable.io/scenario2');
};
var checkData = function (data) {
if(!data.endpoints || !data.endpoints.length) {
return $.Deferred().reject('no endpoints').promise();
}
data.base = data.base || {};
data.base.frequency = data.base.frequency || 1000;//default value
};
var waitForTimeout = function(data) {
return $.Deferred(function(dfrd) {
setTimeout(function() {
dfrd.resolve(data.endpoints);
}, data.base.frequency);
}).promise();
};
var callApi = function(endpoints) {
console.log('Calling API with given instructions ...');
return $.when.apply(null, endpoints.map(ep) {
return $.ajax({
type: ep.method,
dataType: ep.type,
url: ep.endpoint
}).then(null, function(jqXHR, textStatus, errorThrown) {
return textStatus;
});
}).then(function() {
//convert arguments to an array of results
return $.map(arguments, function(arg) {
return arg[0];
});
});
};
var handleResults = function(results) {
// results is an array of data values/objects returned by the ajax calls.
console.log("Handling data ...");
...
};
// The 'run' method
var run = function() {
getScenario()
.then(checkData)
.then(waitForTimeout)
.then(callApi)
.then(handleResults)
.then(null, function(reason) {
console.error(reason);
})
.then(run);
};
return {
run : run
}
})(jQuery);
App.run();
This will stop on error but could be easily adapted to continue.
I'll try to answer your question using KrisKowal's q since I'm not very proficient with the promises generated by jQuery.
First of all I'm not sure whether you want to solve the array of promises in series or in parallel, in the solution proposed I resolved all of them in parallel :), to solve them in series I'd use Q's reduce
function getScenario() { ... }
function ajaxRequest(instruction) { ... }
function createPromisifiedInstruction(instruction) {
// delay with frequency, not sure why you want to do this :(
return Q.delay(instruction.frequency)
.then(function () {
return this.ajaxRequest(instruction);
});
}
function run() {
getScenario()
.then(function (data) {
var promises = [];
var instruction;
var i;
for (i = 0; i < data.endpoints.length; i += 1) {
instruction = {
method: data.endpoints[i].method,
type: data.endpoints[i].type,
endpoint: data.endpoints[i].endPoint,
frequency: data.base.frequency
};
promises.push(createPromisifiedInstruction(instruction));
}
// alternative Q.allSettled if all the promises don't need to
// be fulfilled (some of them might be rejected)
return Q.all(promises);
})
.then(function (instructionsResults) {
// instructions results is an array with the result of each
// promisified instruction
})
.then(run)
.done();
}
run();
Ok let me explain the solution above:
first of all assume that getScenario gets you the initial json you start with (actually returns a promise which is resolved with the json)
create the structure of each instruction
promisify each instruction, so that each one is actually a promise whose
resolution value will be the promise returned by ajaxRequest
ajaxRequest returns a promise whose resolution value is the result of the request, which also means that createPromisifiedInstruction resolution value will be the resolution value of ajaxRequest
Return a single promise with Q.all, what it actually does is fulfill itself when all the promises it was built with are resolved :), if one of them fails and you actually need to resolve the promise anyways use Q.allSettled
Do whatever you want with the resolution value of all the previous promises, note that instructionResults is an array holding the resolution value of each promise in the order they were declared
Reference: KrisKowal's Q
Try utilizing deferred.notify within setTimeout and Number(settings.frequency) * (1 + key) as setTimeout duration; msg at deferred.notify logged to console at deferred.progress callback , third function argument within .then following timeout
var App = (function ($) {
var getScenario = function () {
console.log("Getting scenario ...");
return $.get("http://demo3858327.mockable.io/scenario2");
};
var mapToInstruction = function (data) {
var res = $.map(data.endpoints, function(settings, key) {
return {
method:settings.method,
type:settings.type,
endpoint:settings.endPoint,
frequency:data.base.frequency
}
});
console.log("Instructions recieved:", res);
return res
};
var waitForTimeout = function(instruction) {
var res = $.when.apply(instruction,
$.map(instruction, function(settings, key) {
return new $.Deferred(function(dfd) {
setTimeout(function() {
dfd.notify("Waiting for "
+ settings.frequency
+ " ms")
.resolve(settings);
}, Number(settings.frequency) * (1 + key));
}).promise()
})
)
.then(function() {
return this
}, function(err) {
console.log("error", err)
}
, function(msg) {
console.log("\r\n" + msg + "\r\nat " + $.now() + "\r\n")
});
return res
};
var callApi = function(instruction) {
console.log("Calling API with given instructions ..."
, instruction);
var res = $.when.apply(instruction,
$.map(instruction, function(request, key) {
return request.then(function(settings) {
return $.ajax({
type: settings.method,
dataType: settings.type,
url: settings.endpoint
});
})
})
)
.then(function(data) {
return $.map(arguments, function(response, key) {
return response[0]
})
})
return res
};
var handleResults = function(data) {
console.log("Handling data ..."
, JSON.stringify(data, null, 4));
return data
};
var run = function() {
getScenario()
.then(mapToInstruction)
.then(waitForTimeout)
.then(callApi)
.then(handleResults)
.then(run);
};
return {
// This will expose only the run method
// but will keep all other functions private
run : run
}
})($);
// ... And start the app
App.run();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
jsfiddle http://jsfiddle.net/3Lddzp9j/13/
You have a return statement in the loop in your waitForTimeout function. This means that the function is going to return after the first iteration of the loop, and that is where you are going wrong.
You're also using the deferred antipattern and are using promises in places where you don't need them. You don't need to return a promise from a then handler unless there's something to await.
The key is that you need to map each of your instructions to a promise. Array#map is perfect for this. And please use a proper promise library, not jQuery promises (edit but if you absolutely must use jQuery promises...):
var App = (function ($) {
// Gets the scenario from the API
// NOTE: this returns a promise
var getScenario = function () {
console.log('Getting scenario ...');
return $.get('http://demo3858327.mockable.io/scenario');
};
// mapToInstructions is basically unnecessary. each instruction does
// not need its own timeout if they're all the same value, and you're not
// reshaping the original values in any significant way
// This wraps the setTimeout into a promise, again
// so we can chain it
var waitForTimeout = function(data) {
var d = $.Deferred();
setTimeout(function () {
d.resolve(data.endpoints);
}, data.base.frequency);
return d.promise();
};
var callApi = function(instruction) {
return $.ajax({
type: instruction.method,
dataType: instruction.type,
url: instruction.endPoint
});
};
// Final step: call the API from the
// provided instructions
var callApis = function(instructions) {
console.log(instructions);
console.log('Calling API with given instructions ...');
return $.when.apply($, instructions.map(callApi));
};
var handleResults = function() {
var data = Array.prototype.slice(arguments);
console.log("Handling data ...");
};
// The 'run' method
var run = function() {
getScenario()
.then(waitForTimeout)
.then(callApis)
.then(handleResults)
.then(run);
};
return {
run : run
}
})($);
App.run();

Categories

Resources