My client-side Javascript app needs a certain data structure from the server side ("lists"). Several different places in the app need access to the lists. On startup, these places all make the request, which results in several concurrent requests to the server for the same data structure.
I'd like to issue only one request to the server and have all of requesters simply get a promise that resolves when the request comes back.
I think the method is to create an array or stack of promises. If the first request is in-flight, then each subsequent request causes a promise to get created and added to the stack. When the response comes back, the system runs through the stack and resolves all the promises with the result.
The problem is that I can't figure out the right syntax to get this to work. This is what I have so far:
let lists = [];
let loading = false;
let promises = [];
function getLists() {
if (lists.length > 0) {
// return cached copy
return Promise.resolve(lists);
}
/*
This method can get called several times in quick succession on startup.
To prevent sending multiple requests to the server, we maintain
a stack of promises. If there is already a request in-flight, then just
add a promise to the stack and return it. Then resolve all promises
in the stack when the request returns.
*/
let prom = new Promise(); // BAD, NOT ALLOWED
promises.push(prom);
if (!loading) {
callListsApi(); // async call, resolves promises
}
return prom;
}
function callListsApi() {
loading = true;
axios.get("/lists").then(
response => {
loading = false;
if (!response.data || response.data.length == 0) {
lists = [];
} else {
lists = response.data;
}
for (let i = 0; i < promises.length; i++) {
promises[i].resolve(lists); // give all the callers their lists
}
promises = [];
},
error => {
loading = false;
util.handleAxiosError(error);
let msg = util.getAxiosErrorText(error);
for (let i = 0; i < promises.length; i++) {
promises[i].reject(msg);
}
promises = [];
}
);
}
This doesn't work because you can't create a bare Promise() without putting some kind of executor function in it.
How can I rewrite this so it works?
Figured it out. The answer isn't to create a stack of promises. It's just to return to each requestor the same promise over and over. We maintain one main promise, which exists only when a request is in-flight. This is way simpler.
let lists = [];
let listPromise = null;
function getLists() {
if (lists.length > 0) {
// return cached copy
return Promise.resolve(lists);
}
if (listPromise != null) {
// if we get here, then there is already a request in-flight
return listPromise;
}
listPromise = callListsApi();
return listPromise;
}
function callListsApi() {
return axios.get("/lists").then(
response => {
if (!response.data || response.data.length == 0) {
lists = [];
} else {
lists = response.data;
}
listPromise = null;
return lists;
},
error => {
util.handleAxiosError(error);
listPromise = null;
return util.getAxiosErrorText(error);
}
);
}
Related
I want to fetch multiple images and turn them in blob. I'm a newbie about promises, I've tried but I can't get through.
Here below, a single .fetch() promise
fetch('http://cors.io/?u=http://alistapart.com/d/_made/d/ALA350_appcache_300_960_472_81.jpg')
.then(function(response) {
return response.blob();
})
.then(function(myBlob) {
var objectURL = URL.createObjectURL(myBlob);
document.getElementById('myImage').src = objectURL;
});
Now multiple .fetch() promise (don't work)
var promises = [];
for (var i = values.length - 1; i >= 0; i--) {
promises.push(fetch(values[i]));
}
Promise
.all(promises)
.then(function(response) {
for (var i = response.length - 1; i >= 0; i--) {
return response[i].blob();
}
})
.then(function(blob) {
console.log(blob.length); //undefined !!!
for (var i = blob.length - 1; i >= 0; i--) {
console.log(blob[i]);
lcl_images[i].value = URL.createObjectURL(blob[i]);
document.getElementById(lcl_images[i].id).src = objectURL;
}
})
.catch(function(error) {
console.log(error);
});
It is a general rule that a wholly synchronous intermediate step in the success path of a promise chain can be amalgamated with the next step, allowing one then() to be omitted from the chain.
There is actually a proviso on that statement, involving intermediate catches, but it will suffice for this answer.
So, if the .blob() method is geuinely synchronous (it returns a value), only one .then() is required, not two.
Here are two approaches, both of which exploit Array.prototype.map(), and both should work (though they will differ under error conditions):
1. Simple .map() with detail in Promise.all()
var promises = values.reverse().map(fetch); // you may need .reverse(), maybe not. I'm not 100% sure.
return Promise.all(promises).then(function(responses) {
responses.forEach(function(r, i) {
var imageObj = lcl_images[i],
element = document.getElementById(imageObj.id);
imageObj.value = URL.createObjectURL(r.blob());
if(element) { //safety
element.src = imageObj.value;
}
});
return responses; // here, return whatever you want to be made available to the caller.
}).catch(function(error) {
console.log(error);
});
If you prefer, you can write :
return Promise.all(values.reverse().map(fetch)).then(function(responses) {
// ...
});
2. Detail in .map() followed a simple Promise.all()
var promises = values.reverse().map(function(val, i) {
return fetch(val).then(function(result) {
var imageObj = lcl_images[i],
element = document.getElementById(imageObj.id);
imageObj.value = URL.createObjectURL(result.blob());
if(element) { //safety
element.src = imageObj.value;
}
return result; // here, return whatever you want to be made available to the caller.
});
});
return Promise.all(promises).catch(function(error) { // return a promise to the caller
console.log(error);
});
Notes:
(1) will fail completely if any one fetch() fails.
(2) will perform all the imageObj.value ... and element.src = ... stuff for all successful fetches even if one or more fetch()... fails. Any single failure will cause Promise.all(promises) to return a rejected promise.
(1) or (2) may be more appropriate depending on what you want.
There are other error handling possibilities.
If neither approach works, then the most reasonable explanation would be that the .blob() method returns a promise, not a value.
You are returning from the then handler after first response, instead what you need to do is to return the list of blobs:
Promise
.all(promises)
.then(function(response) {
// CHANGED HERE
var blobPromises = [];
for (var i = response.length - 1; i >= 0; i--) {
blobPromises.push(response[i].blob());
}
return Promise.all(blobPromises);
})
.then(function(blob) {
console.log(blob.length);
for (var i = blob.length - 1; i >= 0; i--) {
lcl_images[i].value = URL.createObjectURL(blob[i]);
document.getElementById(lcl_images[i].id).src = objectURL;
}
})
.catch(function(error) {
console.log(error);
});
Problem 1: only one API request is allowed at a given time, so the real network requests are queued while there's one that has not been completed yet. An app can call the API level anytime and expecting a promise in return. When the API call is queued, the promise for the network request would be created at some point in the future - what to return to the app? That's how it can be solved with a deferred "proxy" promise:
var queue = [];
function callAPI (params) {
if (API_available) {
API_available = false;
return doRealNetRequest(params).then(function(data){
API_available = true;
continueRequests();
return data;
});
} else {
var deferred = Promise.defer();
function makeRequest() {
API_available = false;
doRealNetRequest(params).then(function(data) {
deferred.resolve(data);
API_available = true;
continueRequests();
}, deferred.reject);
}
queue.push(makeRequest);
return deferred.promise;
}
}
function continueRequests() {
if (queue.length) {
var makeRequest = queue.shift();
makeRequest();
}
}
Problem 2: some API calls are debounced so that the data to be sent is accumulated over time and then is sent in a batch when a timeout is reached. The app calling the API is expecting a promise in return.
var queue = null;
var timeout = 0;
function callAPI2(data) {
if (!queue) {
queue = {data: [], deferred: Promise.defer()};
}
queue.data.push(data);
clearTimeout(timeout);
timeout = setTimeout(processData, 10);
return queue.deferred.promise;
}
function processData() {
callAPI(queue.data).then(queue.deferred.resolve, queue.deferred.reject);
queue = null;
}
Since deferred is considered an anti-pattern, (see also When would someone need to create a deferred?), the question is - is it possible to achieve the same things without a deferred (or equivalent hacks like new Promise(function (resolve, reject) {outerVar = [resolve, reject]});), using the standard Promise API?
Promises for promises that are yet to be created
…are easy to build by chaining a then invocation with the callback that creates the promise to a promise represents the availability to create it in the future.
If you are making a promise for a promise, you should never use the deferred pattern. You should use deferreds or the Promise constructor if and only if there is something asynchronous that you want to wait for, and it does not already involve promises. In all other cases, you should compose multiple promises.
When you say
When the API call is queued, the promise for the network request would be created at some point in the future
then you should not create a deferred that you can later resolve with the promise once it is created (or worse, resolve it with the promises results once the promise settles), but rather you should get a promise for the point in the future at which the network reqest will be made. Basically you're going to write
return waitForEndOfQueue().then(makeNetworkRequest);
and of course we're going to need to mutate the queue respectively.
var queue_ready = Promise.resolve(true);
function callAPI(params) {
var result = queue_ready.then(function(API_available) {
return doRealNetRequest(params);
});
queue_ready = result.then(function() {
return true;
});
return result;
}
This has the additional benefit that you will need to explicitly deal with errors in the queue. Here, every call returns a rejected promise once one request failed (you'll probably want to change that) - in your original code, the queue just got stuck (and you probably didn't notice).
The second case is a bit more complicated, as it does involve a setTimeout call. This is an asynchronous primitive that we need to manually build a promise for - but only for the timeout, and nothing else. Again, we're going to get a promise for the timeout, and then simply chain our API call to that to get the promise that we want to return.
function TimeoutQueue(timeout) {
var data = [], timer = 0;
this.promise = new Promise(resolve => {
this.renew = () => {
clearTimeout(timer);
timer = setTimeout(resolve, timeout);
};
}).then(() => {
this.constructor(timeout); // re-initialise
return data;
});
this.add = (datum) => {
data.push(datum);
this.renew();
return this.promise;
};
}
var queue = new TimeoutQueue(10);
function callAPI2(data) {
return queue.add(data).then(callAPI);
}
You can see here a) how the debouncing logic is completely factored out of callAPI2 (which might not have been necessary but makes a nice point) and b) how the promise constructor only concerns itself with the timeout and nothing else. It doesn't even need to "leak" the resolve function like a deferred would, the only thing it makes available to the outside is that renew function which allows extending the timer.
When the API call is queued, the promise for the network request would
be created at some point in the future - what to return to the app?
Your first problem can be solved with promise chaining. You don't want to execute a given request until all prior requests have finished and you want to execute them serially in order. This is exactly the design pattern for promise chaining. You can solve that one like this:
var callAPI = (function() {
var p = Promise.resolve();
return function(params) {
// construct a promise that chains to prior callAPI promises
var returnP = p.then(function() {
return doRealNetRequest(params);
});
// make sure the promise we are chaining to does not abort chaining on errors
p = returnP.then(null, function(err) {
// handle rejection locally for purposes of continuing chaining
return;
});
// return the new promise
return returnP;
}
})();
In this solution, a new promise is actually created immediately with .then() so you can return that promise immediately - there is no need to create a promise in the future. The actual call to doRealNetRequest() is chained to this retrurned .then() promise by returning its value in the .then() handler. This works because, the callback we provide to .then() is not called until some time in the future when the prior promises in the chain have resolved, giving us an automatic trigger to execute the next one in the chain when the prior one finishes.
This implementation assumes that you want queued API calls to continue even after one returns an error. The extra few lines of code around the handle rejection comment are there to make sure the chain continues even where a prior promise rejects. Any rejection is returned back to the caller as expected.
Here's a solution to your second one (what you call debounce).
the question is - is it possible to achieve the same things without a
deferred (or equivalent hacks like new Promise(function (resolve,
reject) {outerVar = [resolve, reject]});), using the standard Promise
API?
As far as I know, the debouncer type of problem requires a little bit of a hack to expose the ability to trigger the resolve/reject callbacks somehow from outside the promise executor. It can be done a little cleaner than you propose by exposing a single function that is within the promise executor function rather than directly exposing the resolve and reject handlers.
This solution creates a closure to store private state that can be used to manage things from one call to callAPI2() to the next.
To allow code at an indeterminate time in the future to trigger the final resolution, this creates a local function within the promise executor function (which has access to the resolve and reject functions) and then shares that to the higher (but still private) scope so it can be called from outside the promise executor function, but not from outside of callAPI2.
var callAPI2 = (function() {
var p, timer, trigger, queue = [];
return function(data) {
if (!p) {
p = new Promise(function(resolve) {
// share completion function to a higher scope
trigger = function() {
resolve(queue);
// reinitialize for future calls
p = null;
queue = [];
}
}).then(callAPI);
}
// save data and reset timer
queue.push(data);
clearTimeout(timer);
setTimeout(trigger, 10);
return p;
}
})();
You can create a queue, which resolves promises in the order placed in queue
window.onload = function() {
(function(window) {
window.dfd = {};
that = window.dfd;
that.queue = queue;
function queue(message, speed, callback, done) {
if (!this.hasOwnProperty("_queue")) {
this._queue = [];
this.done = [];
this.res = [];
this.complete = false;
this.count = -1;
};
q = this._queue,
msgs = this.res;
var arr = Array.prototype.concat.apply([], arguments);
q.push(arr);
msgs.push(message);
var fn = function(m, s, cb, d) {
var j = this;
if (cb) {
j.callback = cb;
}
if (d) {
j.done.push([d, j._queue.length])
}
// alternatively `Promise.resolve(j)`, `j` : `dfd` object
// `Promise` constructor not necessary here,
// included to demonstrate asynchronous processing or
// returned results
return new Promise(function(resolve, reject) {
// do stuff
setTimeout(function() {
div.innerHTML += m + "<br>";
resolve(j)
}, s || 0)
})
// call `cb` here, interrupting queue
.then(cb ? j.callback.bind(j, j._queue.length) : j)
.then(function(el) {
console.log("queue.length:", q.length, "complete:", el.complete);
if (q.length > 1) {
q.splice(0, 1);
fn.apply(el, q[0]);
return el
} else {
el._queue = [];
console.log("queue.length:", el._queue.length
, "complete:", (el.complete = !el._queue.length));
always(promise(el), ["complete", msgs])
};
return el
});
return j
}
, promise = function(t) {
++t.count;
var len = t._queue.length,
pending = len + " pending";
return Promise.resolve(
len === 1
? fn.apply(t, t._queue[0]) && pending
: !(t.complete = len === 0) ? pending : t
)
}
, always = function(elem, args) {
if (args[0] === "start") {
console.log(elem, args[0]);
} else {
elem.then(function(_completeQueue) {
console.log(_completeQueue, args);
// call any `done` callbacks passed as parameter to `.queue()`
Promise.all(_completeQueue.done.map(function(d) {
return d[0].call(_completeQueue, d[1])
}))
.then(function() {
console.log(JSON.stringify(_completeQueue.res, null, 2))
})
})
}
};
always(promise(this), ["start", message, q.length]);
return window
};
}(window));
window
.dfd.queue("chain", 1000)
.dfd.queue("a", 1000)
.dfd.queue("b", 2000)
.dfd.queue("c", 2000, function callback(n) {
console.log("callback at queue index ", n, this);
return this
}, function done(n) {
console.log("all done callback attached at queue index " + n)
})
.dfd.queue("do", 2000)
.dfd.queue("other", 2000)
.dfd.queue("stuff", 2000);
for (var i = 0; i < 10; i++) {
window.dfd.queue(i, 1000)
};
window.dfd.queue.apply(window.dfd, ["test 1", 5000]);
window.dfd.queue(["test 2", 1000]);
var div = document.getElementsByTagName("div")[0];
var input = document.querySelector("input");
var button = document.querySelector("button");
button.onclick = function() {
window.dfd.queue(input.value, 0);
input.value = "";
}
}
<input type="text" />
<button>add message</button>
<br>
<div></div>
I understand using promises in simple scenarios but currently really confused on how to implement something when using a for loop and some updates to local sqlite database.
Code is as follows
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
var q = $q.defer();
for (var item in surveys) {
var survey = surveys[item];
// created as a closure so i can pass in the current item due to async process
(function(survey) {
ajaxserviceAPI.postSurvey(survey).then(function(response) {
//from response update local database
surveyDataLayer.setLocalSurveyServerId(survey, response.result).then(function() {
q.resolve; // resolve promise - tried only doing this when last record also
})
});
})(survey) //pass in current survey used to pass in item into closure
}
return q.promise;
}).then(function() {
alert('Done'); // This never gets run
});
Any help or assistance would be appreciated. I'm probably struggling on how best to do async calls within loop which does another async call to update and then continue once completed.
at least promises have got me out of callback hell.
Cheers
This answer will get you laid at JS conferences (no guarantees though)
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
return Promise.all(Object.keys(surveys).map(function(key) {
var survey = surveys[key];
return ajaxserviceAPI.postSurvey(survey).then(function(response){
return surveyDataLayer.setLocalSurveyServerId(survey, response.result);
});
}));
}).then(function() {
alert('Done');
});
This should work (explanations in comments):
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
// array to store promises
var promises = [];
for (var item in surveys) {
var survey = surveys[item];
// created as a closure so i can pass in the current item due to async process
(function(survey) {
var promise = ajaxserviceAPI.postSurvey(survey).then(function(response){
//returning this promise (I hope it's a promise) will replace the promise created by *then*
return surveyDataLayer.setLocalSurveyServerId(survey, response.result);
});
promises.push(promise);
})(survey); //pass in current survey used to pass in item into closure
}
// wait for all promises to resolve. If one fails nothing resolves.
return $q.all(promises);
}).then(function() {
alert('Done');
});
Awesome tutorial: http://ponyfoo.com/articles/es6-promises-in-depth
You basically want to wait for all of them to finish before resolving getSurveysToUpload, yes? In that case, you can return $q.all() in your getSurveysToUpload().then()
For example (not guaranteed working code, but you should get an idea):
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
var promises = [];
// This type of loop will not work in older IEs, if that's of any consideration to you
for (var item in surveys) {
var survey = surveys[item];
promises.push(ajaxserviceAPI.postSurvey(survey));
}
var allPromise = $q.all(promises)
.then(function(responses) {
// Again, we want to wait for the completion of all setLocalSurveyServerId calls
var promises = [];
for (var index = 0; index < responses.length; index++) {
var response = responses[index];
promises.push(surveyDataLayer.setLocalSurveyServerId(survey, response.result));
}
return $q.all(promises);
});
return allPromise;
}).then(function() {
alert('Done'); // This never gets run
});
I have tried to do this with q as well as async, but haven't been able to seem to make it work. After trying those I tried my own way. I didn't think this would work, but I thought I would give it a try. I am confused since there is a callback within a callback in a sense. Here is the function I am wanting to do:
var getPrice = function(theData) {
var wep = theData.weapon;
var completed = 0;
for (i = 0; i < theData.skins.length; i++) {
var currSkin = theData.skins[i];
theData.skinData[currSkin] = {};
for (k = 0; k < wears.length; k++) {
csgomarket.getSinglePrice(wep, currSkin, wears[k], false,
function(err, data) {
completed++;
if (!err) {
theData.skinData[data.skin][data.wear] = data;
}
if (completed === theData.skins.length*wears.length) {
return theData;
}
})
}
}
}
I know these kinds of issues are common in javascript as I have ran into them before, but not sure how to solve this one. I am wanting to fill my object with all the data returned by the method:
csgomarket.getSinglePrice(wep, currSkin, wears[k], false,
function(err, data) { });
Since each call to getSinglePrice() sends off a GET request it takes some time for the responses to come back. Any suggestions or help would be greatly appreciated!
First csgomarket.getSinglePrice() needs to be promisified. Here's an adapter function that calls csgomarket.getSinglePrice() and returns a Q promise.
function getSinglePriceAsync(wep, skin, wear, stattrak) {
return Q.Promise(function(resolve, reject) { // may be `Q.promise(...)` (lower case P) depending on Q version.
csgomarket.getSinglePrice(wep, skin, wear, stattrak, function(err, result) {
if(err) {
reject(err);
} else {
resolve(result);
}
});
});
}
Now, you want getPrice() to return a promise that settles when all the individual getSinglePriceAsync() promises settle, which is trivial :
var getPrice = function(theData) {
var promises = [];//array in which to accumulate promises
theData.skins.forEach(function(s) {
theData.skinData[s] = {};
wears.forEach(function(w) {
promises.push(getSinglePriceAsync(theData.weapon, s, w, false).then(function(data) {
theData.skinData[data.skin][data.wear] = data;
}));
});
});
//return a single promise that will settle when all the individual promises settle.
return Q.allSettled(promises).then(function() {
return theData;
});
}
However, theData.skinData[data.skin][data.wear] will simplify slightly to theData.skinData[s][w] :
var getPrice = function(theData) {
var promises = [];//array in which to accumulate promises
theData.skins.forEach(function(s) {
theData.skinData[s] = {}; //
wears.forEach(function(w) {
promises.push(getSinglePriceAsync(theData.weapon, s, w, false).then(function(data) {
theData.skinData[s][w] = data;
}));
});
});
//return a single promise that will settle when all the individual `promises` settle.
return Q.allSettled(promises).then(function() {
return theData;
});
}
This simplification would work because the outer forEach(function() {...}) causes s to be trapped in a closure.
As getPrice() now returns a promise, it must be used as follows :
getPrice(myData).then(function(data) {
// use `data` here.
}).catch(function(e) {
//something went wrong!
console.log(e);
});
Your way to do is very complex.
I think the best way is to do 1 request for all prices. Now, for each price you do a request.
If you have a list (array) with data that's need for the request, the return value should be a list with the prices.
If approach above is not possible, you can read more about batching http requests: http://jonsamwell.com/batching-http-requests-in-angular/
Need some clarification - Are you trying to run this on the Client side? Looks like this is running inside a nodejs program on server side. If so, wouldn't you rather push this logic to the client side and handle with Ajax. I believe the browser is better equipped to handle multiple http request-responses.
Since you didn't post much info about your csgoMarket.getSinglePrice function I wrote one that uses returns a promise. This will then allow you to use Q.all which you should read up on as it would really help in your situation.
I've created an inner and outer loop arrays to hold our promises. This code is completely untested since you didn't put up a fiddle.
var getPrice = function(theData) {
var wep = theData.weapon;
var completed = 0;
var promises_outer = [] //array to hold the arrays of our promises
for (var i = 0; i < theData.skins.length; i++) {
var currSkin = theData.skins[i];
theData.skinData[currSkin] = {};
var promises_inner = [] // an array to hold our promises
for (var k = 0; k < wears.length; k++) { //wears.length is referenced to below but not decalared anywhere in the function. It's either global or this function sits somewhere where it has access to it
promises_inner.push(csgomarket.getSinglePrice(wep, currSkin, wears[k], false))
}
promises_outer.push(promises_inner)
}
promises_outer.forEach(function(el, index){
var currSkin = theData.skins[index]
theData.skinData[currSkin] = {}
Q.all(el).then(function(data){ //data is an array of results in the order you made the calls
if(data){
theData.skinData[data.skin][data.wear] = data
}
})
})
}
var csgomarket = {}
csgomarket.getSinglePrice = function(wep, currSkin, wears, someBoolean){
return Q.promise(function (resolve, reject){
//do your request or whatever you do
var result = true
var data = {
skin : "cool one",
wear : "obviously"
}
var error = new Error('some error that would be generated')
if(result){
resolve(data)
} else {
reject(error)
}
})
}
So i have this function where i need to combine multiple promises responses, but after some reading i realized promises are async so in this case my loop is going to complete before all the responses do. Should i need to use something like $q.all in this case? How can i improve this piece of code? Thanks..
$scope.messages = [];
function getPastMessages(data) {
angular.forEach(data, function(item) {
Message.get(item.id).then(function(msg) {
if (msg.data.is_private === false) {
User.getPictures(msg.data.user.id).then(function(pics) {
msg.data.user.pictures = pics.data;
});
} else {
User.get(msg.data.im.sender).then(function(sender) {
msg.data.im.sender = sender.data;
User.get(msg.data.im.reciever).then(function(reciever) {
msg.data.im.reciever = reciever.data;
});
});
}
console.log(msg.data); // SHOW 4 OBJECTS CORRECT
$scope.messages.push(msg.data);
console.log($scope.messages); // SHOW ARRAY OF 6 OBJECTS ????????
})
});
};
Without a working example it was difficult to fully understand the context of your code, but you can do something similar to this.
The basic idea is to create a list of promises that you need to wait for. Each of the promises within this list should return a result (presumably msg.data). Using $q.all, you'll get a list of results (one from each promise) at the end. Note that things returned within a .then get wrapped in promises if they aren't already promises.
$scope.messages = [];
function getPastMessages(data) {
var promises = [];
angular.forEach(data, function(item) {
promises.push(getMessage(item));
});
return $q.all(promises);
}
function getMessage(item) {
return Message.get(item.id).then(function(msg) {
if (msg.data.is_private === false) {
return User.getPictures(msg.data.user.id).then(function(pics) {
msg.data.user.pictures = pics.data;
return msg.data;
});
} else {
return User.get(msg.data.im.sender).then(function(sender) {
msg.data.im.sender = sender.data;
return User.get(msg.data.im.reciever).then(function(reciever) {
msg.data.im.reciever = reciever.data;
return msg.data;
});
});
}
});
}
Usage:
getPastMessages(data).then(function(results) {
for (var i = 0; i < results.length; i++) {
$scope.messages.push(results[i]);
}
});
You can only rely on promises to have resolved when you're inside their callback functions.
var messages = [];
somethingAsync().then(function(data){
messages.push(data);
});
console.log(messages.length)
might return 0 or 1 depending on how long somethingAsync takes; you can't rely on it having completed.
Instead you should do your debugging from inside the callback function:
var messages = [];
somethingAsync().then(function(data){
messages.push(data);
console.log(messages.length)
});
This will always return 1.