Embed Promise within a Promise - javascript

I'm new to both node.js and Promise functionality so please forgive me if this question is really foolish.
I'm trying to make a child Promise call forEach child within a Parent call (if that makes sense).
This is my code:
return new Promise(function(resolve, reject) {
var authorMapArray = new Array
db.sequelize.query(authorQuery, {
replacements: queryParams
}).spread(function(authorSitemap) {
authorSitemap.forEach(function(obj) {
/*
return new Promise (function(resolve, reject){
var thisQuery = photoQuery + ' AND author = ' + obj.id.toString();
db.sequelize.query(thisQuery, {
queryParams
}).spread(function(authorImages) {
var authorImageArray = new Array;
authorImages.forEach(function(obj) {
var imgLink = { url: imgHost + obj.img_id + '.jpg', title : img_tags }
authorImageArray.push(imgLink);
})
});
resolve(authorImageArray);
});
*/
var authorLink = { url: 'author/' + obj.id, /*img: authorImageArray,*/ changefreq: 'weekly', priority: 0.6, lastmodrealtime: true }
siteMapArray.push(authorLink);
});
resolve(siteMapArray);
//and finally create it
createSiteMap(siteMapArray);
});
})
You'll note, the section in the middle is commented out. When I run the code like this I get the results I expect, that is the authorLink added to the sitemap. When I uncomment the code (in order to include the images associated with the author in the sitemap), not even the authorlinks are added.
How do I get the images for the author included within their record?
EDIT
This is the more complete code:
function createSiteMap(myURLs) {
var rows = 10000;
var totalMaps = Math.trunc(myURLs.length/rows)+1;
var today = new Date();
var mySitemaps = new Array;
for (var i=1; i<totalMaps+1; i++) {
var filename = "public/sitemap-" + i.toString() + ".xml";
var sitemap = sm.createSitemap({
hostname: hostname,
cacheTime: 600000, //600 sec (10 min) cache purge period
urls: myURLs.slice((i-1)*rows,i*rows)
});
fs.writeFileSync(filename, sitemap.toString());
mySitemaps.push(filename);
}
// this needs to create sitemap tags not url tags
var smi = sm.buildSitemapIndex({
urls: mySitemaps
});
fs.writeFileSync("public/sitemap.xml", smi.toString());
process.exit();
}
function uniq(a) {
var seen = {};
return a.filter(function(item) {
return seen.hasOwnProperty(item) ? false : (seen[item] = true);
});
}
function getPhotos() {
return new Promise(function(resolve, reject) {
var siteMapArray = new Array()
var tags = new Array()
siteMapArray.push ({ url: '/' , changefreq: 'weekly', priority: 0.8, lastmodrealtime: true, lastmodfile: 'views/home.hbs' },)
db.sequelize.query(photoQuery, {
replacements: queryParams
}).spread(function(makeSiteMap) {
makeSiteMap.forEach(function(obj) {
// images for sitemap
var img_tags = obj.tags.replace(/,/g , " ");
var imgLink = { url: imgHost + obj.img_id + '.jpg', title : img_tags }
var siteLink = { url: 'photo/' + obj.img_id, img: imgLink, changefreq: 'weekly', priority: 0.6, lastmodrealtime: true }
siteMapArray.push(siteLink);
obj.tags = obj.tags.split(',').map(function(e) {
return e.trim().split(' ').join('+');
});
for (var tag in obj.tags) {
tags.push(obj.tags[tag])
}
});
resolve (siteMapArray);
//tags for sitemap
var uniqueTags = uniq(tags);
for (var tag in uniqueTags) {
var siteLink = { url: '/search/' + uniqueTags[tag], changefreq: 'weekly', priority: 0.8, lastmodrealtime: true }
siteMapArray.push (siteLink);
}
//now author tags
return new Promise(function(resolve, reject) {
var authorMapArray = new Array
db.sequelize.query(authorQuery, {
replacements: queryParams
}).spread(function(authorSitemap) {
authorSitemap.forEach(function(obj) {
/*
return new Promise (function(resolve, reject){
var thisQuery = photoQuery + ' AND author = ' + obj.id.toString();
db.sequelize.query(thisQuery, {
queryParams
}).spread(function(authorImages) {
var authorImageArray = new Array;
authorImages.forEach(function(obj) {
var imgLink = { url: imgHost + obj.img_id + '.jpg', title : img_tags }
authorImageArray.push(imgLink);
})
});
resolve(authorImageArray);
});
*/
var authorLink = { url: 'author/' + obj.id, /*img: authorImageArray,*/ changefreq: 'weekly', priority: 0.6, lastmodrealtime: true }
siteMapArray.push(authorLink);
});
resolve(siteMapArray);
//and finally create it
createSiteMap(siteMapArray);
});
})
});
});
};
getPhotos();

Okay, let's assume that you want something like this:
function getSiteMapArray() {
// return a promise that resolves to the siteMapArray
}
First step would be to rewrite it without using new Promise() - you shouldn't be needing this one very often, since most of work with promises is just chaining .then() calls which is much more readable.
Note that .spread() is just a .then() with sugar on top. The sugar is not standard Promise syntax, but rather an addon from bluebird that sequelize recommends to use. These are equivalent for a promise that resolves with array with 2 values:
something.then(resultArray => ...);
something.spread((resultItem1, resultItem2) => ...);
(I'm going to use arrow functions, is that OK?)
So the first step is to get rid of new Promise() as promised, before we start incorporating the code from your comments:
function getSiteMapArray() {
var authorMapArray = new Array();
return db.sequelize
.query(authorQuery, {
replacements: queryParams
})
.spread(authorSitemap => {
authorSitemap.forEach(function(obj) {
var authorLink = {
url: "author/" + obj.id,
/*img: authorImageArray,*/
changefreq: "weekly",
priority: 0.6,
lastmodrealtime: true
};
siteMapArray.push(authorLink);
});
return siteMapArray;
});
}
Simple enough?
We use .query() to get a promise of results,
then we use .then() or .spread() passing a callback that processes the results,
spread() returns a new promise that resolves when we're done with everything, and this promise is the result of getSiteMapArray(). It's going to resolve with the value from return siteMapArray.
We could simplify one step further with map() instead of forEach which is recommended whenever you want to transform each element in an array:
function getSiteMapArray() {
return db.sequelize
.query(authorQuery, {
replacements: queryParams
})
.spread(authorSitemap => {
return authorSitemap.map(obj => ({
url: "author/" + obj.id,
/*img: authorImageArray,*/
changefreq: "weekly",
priority: 0.6,
lastmodrealtime: true
}));
});
}
So that was the easy part, now how do we incorporate the authorImage query here?
Let me extract a helper first:
function getSiteMapArray() {
return db.sequelize
.query(authorQuery, {
replacements: queryParams
})
.spread(authorSitemap => {
return authorSitemap.map(getAuthorDescription);
});
}
function getAuthorDescription(obj) {
return {
url: "author/" + obj.id,
/*img: authorImageArray,*/
changefreq: "weekly",
priority: 0.6,
lastmodrealtime: true
};
}
Now getAuthorDescription is synchronous, but we want it to do a query on its own, so let's rewrite it to async, so that it returns a promise too!
function getAuthorDescription(obj) {
var thisQuery = photoQuery + " AND author = " + obj.id.toString();
return db.sequelize
.query(thisQuery, {
queryParams
})
.spread(function(authorImages) {
var authorImageArray = new Array();
authorImages.forEach(function(obj) {
var imgLink = { url: imgHost + obj.img_id + ".jpg", title: img_tags };
authorImageArray.push(imgLink);
});
return {
url: "author/" + obj.id,
img: authorImageArray,
changefreq: "weekly",
priority: 0.6,
lastmodrealtime: true
};
});
}
Another great case to use .map() but I'll leave that one for you.
Back to the original code:
function getSiteMapArray() {
return db.sequelize
.query(authorQuery, {
replacements: queryParams
})
.spread(authorSitemap => {
return authorSitemap.map(getAuthorDescription); // !!!
});
}
Wow, now we're in trouble - getAuthorDescription returns a promise, so we're resolving getSiteMapArray with a list of promises, instead of list of values!
We need a way to wait for each of the promises returned from getAuthorDescription to finish, and obtain an array of collected results of all these promises. This way is called Promise.all:
So the code becomes:
function getSiteMapArray() {
return db.sequelize
.query(authorQuery, {
replacements: queryParams
})
.spread(authorSitemap => {
return Promise.all(authorSitemap.map(getAuthorDescription));
});
}
Let me know if this helps!

There are couple of issues in your implementation. I will suggest not to use [].foreach for child promise. make separate method for child promise and called it for each authorSitemap using promise.all.
Below is sample implementation with update.
return new Promise(function(resolve, reject) {
var authorMapArray = new Array
db.sequelize.query(authorQuery, {
replacements: queryParams
}).spread(function(authorSitemap) {
return Promise.all(authorSitemap.map(GetAuthor))
.then(function(authorImageArray){
var authorLink = { url: 'author/' + obj.id, img: authorImageArray, changefreq: 'weekly', priority: 0.6, lastmodrealtime: true }
siteMapArray.push(authorLink);
createSiteMap(siteMapArray);
resolve(siteMapArray);
})
.catch(function(error){
reject(error);
})
});
})
function GetAuthor(obj) {
return new Promise(function(reject,resolve){
var thisQuery = photoQuery + ' AND author = ' + obj.id.toString();
db.sequelize.query(thisQuery, { queryParams})
.spread(function(authorImages) {
var authorImageArray = new Array;
authorImages.forEach(function(obj) {
var imgLink = { url: imgHost + obj.img_id + '.jpg', title : img_tags }
authorImageArray.push(imgLink);
})
resolve(authorImageArray);
});
})
}

Related

Cannot access data from component method

I tried components methods in vue js. My code like this.
const Thread = Vue.component('threadpage', function(resolve) {
$.get('templates/thread.html').done(function(template) {
resolve({
template: template,
data: function() {
return {
data: {
title: "Data Table",
count: this.GetData
}
};
},
methods: {
GetData: function() {
var data = {
username : "newshubid",
data : {
page : 0,
length : 10,
schedule : "desc"
}
};
var args = {"data" : JSON.stringify(data)};
var params = $.param(args);
var url = "http://example-url";
var result;
DoXhr(url, params, function(response){
result = JSON.parse(response).data;
console.log("load 1", result);
});
setTimeout(function () {
console.log("load 2", result);
return result;
}, 1000);
}
},
created: function(){
this.GetData();
}
});
});
});
But, when I trying to use {{ data.count }} in template. Not showing result what i want. Even I tried return result in GetData.
Whats my problem ? And how to access data from methods ? Please help me, i'm a beginner. Thanks
See the edited code and comments I added below.
You tried to return the result by using return in the function from setTimeout, which won't help you return value from GetData.
Instead, You can just set the value in the callback function of your ajax request.
const Thread = Vue.component('threadpage', function(resolve) {
$.get('templates/thread.html').done(function(template) {
resolve({
template: template,
data: function() {
return {
data: {
title: "Data Table",
// NOTE just set an init value to count, it will be refreshed when the function in "created" invoked.
count: /* this.GetData */ {}
}
};
},
methods: {
GetData: function() {
var data = {
username : "newshubid",
data : {
page : 0,
length : 10,
schedule : "desc"
}
};
var args = {"data" : JSON.stringify(data)};
var params = $.param(args);
var url = "http://example-url";
var result;
var vm = this;
DoXhr(url, params, function(response){
result = JSON.parse(response).data;
// NOTE set data.count to responsed result in callback function directly.
vm.data.count = result;
});
// NOTE I think you don't need code below anymore.
// setTimeout(function () {
// console.log("load 2", result);
// return result;
// }, 1000);
}
},
created: function(){
this.GetData();
}
});
});
});

Wrapping a ajax call

I have a code, that will make inside the select function an ajax request.
oSelect
.select(function (oEvent) {
return oEvent.getSource();
})
.select(function (oControl) {
let oItem = oControl.getSelectedItem();
let aKeys = oItem.getKey().split("/");
return {plant: aKeys[0], wc: aKeys[1]};
})
.select(function (oSelectedItem) {
let oModel = self.getModel("weightProtocolService");
let oPlantFilter = new Filter("Plant", sap.ui.model.FilterOperator.EQ, oSelectedItem.plant);
let oWcFilter = new Filter("WorkCenter", sap.ui.model.FilterOperator.EQ, oSelectedItem.wc);
oModel.read("/CostCenterCalendarSet", {
success: function (oData, oResponse) {
return Rx.Observable.from(oResponse.data.results);
},
error: function (oError) {
return Rx.Observable.throw(oError);
},
filters: [oPlantFilter, oWcFilter]
});
})
.subscribe(function (oKey) {
console.log(oKey);
},
function (err) {
jQuery.sap.log.fatal(err);
});
My problem here is, that it will subscribe first before the ajax response appears.
How can I solve the problem?
Assuming RxJS 5, replace the last select with a mergeMap and return a new observable:
.mergeMap(function (oSelectedItem) {
let oModel = self.getModel("weightProtocolService");
let oPlantFilter = new Filter("Plant", sap.ui.model.FilterOperator.EQ, oSelectedItem.plant);
let oWcFilter = new Filter("WorkCenter", sap.ui.model.FilterOperator.EQ, oSelectedItem.wc);
return new Observable(observer => {
oModel.read("/CostCenterCalendarSet", {
success: function (oData, oResponse) {
observer.next(oResponse.data.results);
},
error: function (oError) {
observer.error(oError);
},
filters: [oPlantFilter, oWcFilter]
});
});
})
If oModel.read returns a promise, then you can simply do the following:
....
return Observable.fromPromise(oModel.read("/CostCenterCalendarSet", {
filters: [oPlantFilter, oWcFilter]
})
);
If oModel.read does not return a promise, then you would need a custom observable:
....
return Observable.create(function(observer) {
oModel.read("/CostCenterCalendarSet", {
success: function (oData, oResponse) {
return observer.onNext(oResponse.data.results); // or just .next(..) in case you are using rxjs5+
},
error: function (oError) {
return observer.onError(oError); // or just .error(..) in case you are using rxjs5+
},
filters: [oPlantFilter, oWcFilter]
});
});

Manage Ember Multiple RSVP Promises on the same route

I am working with the GitHub API in order to load models in a specific route
I am doing two promises one for my personal github details : https://api.github.com/users/user and the other one for my GitHub repositories https://api.github.com/users/user/repos
I can load the models separately but the problem is that i don't figure on how to load both models at the same time in my specific route.
See the code
var IndexRoute = Ember.Route.extend({
model: function(params) {
var url, self, git;
self = this;
git = this.store.createRecord('git',{});
url = 'https://api.github.com/users/user';
return new Ember.RSVP.Promise(function(resolve, reject) {
return Ember.$.getJSON(url, function(data) {
var item = [];
git.setProperties({
name: data.name,
login: data.login,
location: data.location,
company: data.company,
followers: data.followers,
following: data.following
});
item.pushObject(git);
return resolve(item);
});
});
},
model: function(params){
var self, url, repoListProxy;
self = this;
url = 'https://api.github.com/users/user/repos';
repoListProxy = Ember.ArrayProxy.create({
content: []
});
return new Ember.RSVP.Promise(function(resolve, reject) {
return Ember.$.getJSON(url, function(repos) {
if (repos.length) {
repos.toArray().forEach(function(item, index, arr){
var repo;
repo = self.createReposList(item, repoListProxy);
});
repos = repoListProxy.get('content');
return resolve(repos);
}
});
});
},
createReposList: function(repo, arr){
var record
record = this.store.createRecord('repo',{}),
record.setProperties({
name: repo.name,
description: repo.description
})
arr.pushObject(record);
return record;
},
});
How can i load these multiple models with Ember.RSVP.Promise in my specific route?
Since the code you posted is too long to read i didn't implement solution based on it. Here's a common example of loading mutliple promises within a single route in the model hook.
model: function() {
var store = self.get('store');
var someRecord = store.createRecord('foo/bar', {});
var somePromise = imported_promise(someRecord);
return Ember.RSVP.hash({
accessRights: somePromise,
itemData: somePromise.then(function(resolved) {
// Do something here, promise is resolved.
})
myRecord: someRecord,
});
},
Now if you need to access anything from route in the template or controller.
you would first reference to model and then the property.
{{model.myRecord}} or this.get('model.myRecord')
Since you're a nice guy for downvoting me i decided i'd write it for you.
I reccomend using Ic-AJAX : https://github.com/rwjblue/ember-cli-ic-ajax for async calls when you cant use store.find
model: function() {
var store = this.get('store');
var userUrl = 'https://api.github.com/users/user';
var reposUrl = 'https://api.github.com/users/user/repos';
var usersPromise = function() {
return ic.ajax.request(userUrl).then(function(data) {
return store.createRecord('git', {
name: data.name,
login: data.login,
location: data.location,
company: data.company,
followers: data.followers,
following: data.following
})
};
};
var repositoriesPromise = function() {
return ic.ajax.request(reposUrl).then(function(repos) {
return repos.map(function(repo) { // map returns new array no need to write container = [] . container.push(bla)
return store.createRecord('repos', {
name: repo.name,
description: repo.description
});
})
};
}
return Ember.RSVP.hash({
users: usersPromise,
repositories: repositoriesPromise
});
},
Since you're still using a different approach i went ahead and googled its syntax
var gituserPromise = function() {
return Ember.$.ajax(userUrl, {
success: function(data) {
return store.createRecord('git', {
name: data.name,
login: data.login,
location: data.location,
company: data.company,
followers: data.followers,
following: data.following
})
},
error: function(reason) {
reject(reason);
}});
};
return Ember.RSVP.hash({
gitUser: gituserPromise()
});
In hbs i can now do {{model.gitUser.name}}
New link to it http://emberjs.jsbin.com/rutezi/2/edit?html,js,output

How to return value in promise javascript

I have some issue with my code. I need to return a value in promise but don't know how to achived that. I'm newbie in ECS6
Following is my createDate function:
var createData = function (i, object) {
return new Promise(function(resolve) {
var item = object[i]
handleDiease(item.disease).then(function (diseaseId) {
handleCountry(item.country).then(function (countryId) {
handleStatus(lodash.capitalize(item['status(no/failed attempt/yes/friend)'])).then(function (statusId) {
handleType(lodash.capitalize(item["type(o/p)"])).then(function (typeId) {
ContactBLL.create({
name: item.name,
organisation: item.organisation,
email: item.email,
phonenumbers: item.phone,
facebook_url: item.facebook_url,
contactdate: item.date,
da_name: item.donation_affiliate_name,
da_url: item.donation_affiliate_url,
article_url: item.article_url,
//isparticipatefacp: item.isparticipatefacp,
survey_id: item.survey,
notes: item.notes,
fbcontact_diseaseid: diseaseId,
fbcontact_countryid: countryId,
lk_contactstatusid: statusId,
lk_contacttypeid: typeId,
}).then(function (rs) {
if (i < object.length - 2) createData(i + 1, object)
else {
**In else case, i want to return value, i'm using resolve(true) or return true but bold of them not work**
}
});
})
})
})
})
})
}
Following is where I use createDate function:
createData(0, json).then(function(rs) {
console.log(rs)
**It not console anything because I think createData not return or resolve anything**
})
You need to chain your promises, each then should return the promise inside it. Also, avoid explicit construction:
var createData = function (i, object) {
var item = object[i]
var desease = handleDiease(item.disease); // make all requests
var country = handleCountry(item.country); // concurrently, no need to wait
var stat = handleStatus(lodash.capitalize(item['status(no/failed attempt/yes/friend)']));
var type = handleType(lodash.capitalize(item["type(o/p)"]))
// join aggregates several promises, notice the `return` here.
return Promise.join(desease, country, stat, type,
function(deseaseId, countryId, statusId, typeId) {
return ContactBLL.create({ // this needs a `return` too
name: item.name,
organisation: item.organisation,
email: item.email,
phonenumbers: item.phone,
facebook_url: item.facebook_url,
contactdate: item.date,
da_name: item.donation_affiliate_name,
da_url: item.donation_affiliate_url,
article_url: item.article_url,
//isparticipatefacp: item.isparticipatefacp,
survey_id: item.survey,
notes: item.notes,
fbcontact_diseaseid: diseaseId,
fbcontact_countryid: countryId,
lk_contactstatusid: statusId,
lk_contacttypeid: typeId,
});
})
.then(function (rs) { // chain the promise
if (i < rs.length - 2) return createData(i + 1, rs);
else return true;
});
};

How to store values retrieved from a $resource in localStorage?

My service parsing RSS with googleapis and returns a array's Object containing others Objects.
Below, the chrome console ouput :
[Object, Object, Object, Object, Object, Object, Object, Object, Object, Object]
0: Object
1: Object
2: Object
3: Object
But in my controller cannot use localStorage to retrieve data, the console output return only bracket or nothing :
$scope.feeds = FeedList.get();
window.localStorage.setItem('savedData', JSON.stringify($scope.feeds));
console.log('TEST : ' + window.localStorage['savedData']);
console.log('TEST : ' + JSON.parse(window.localStorage.getItem('savedData')));
console.log('TEST : ' + JSON.parse(window.localStorage['savedData']));
Ouput :
TEST : []
TEST :
TEST :
Please, what is wrong ?
service.js
.factory('FeedLoader', function ($resource) {
return $resource('http://ajax.googleapis.com/ajax/services/feed/load', {}, {
fetch: { method: 'JSONP', params: {v: '1.0', callback: 'JSON_CALLBACK'} }
});
})
.service('FeedList', function ($rootScope, FeedLoader) {
var feeds = [];
this.get = function() {
var feedSources = [
{title: 'rss1', url: 'http://www.website.com/rss/feed/rss_feed_25300'},
{title: 'rss2', url: 'http://www.website.com/rss/feed/rss_feed_10720'},
];
if (feeds.length === 0) {
for (var i=0; i<feedSources.length; i++) {
FeedLoader.fetch({q: feedSources[i].url, num: 10}, {}, function (data) {
var feed = data.responseData.feed;
console.log(feed.entries);
feeds.push(feed.entries);
});
}
}
return feeds;
};
})
What's wrong is that FeedList.get() uses asynchrony and $scope.feeds will not be populated right away.
Try this:
$scope.feeds = FeedList.get();
$scope.feeds.then(function () {
// $scope.feeds is done loading now
window.localStorage.setItem('savedData', JSON.stringify($scope.feeds));
console.log('TEST : ' + window.localStorage['savedData']);
console.log('TEST : ' + JSON.parse(window.localStorage.getItem('savedData')));
console.log('TEST : ' + JSON.parse(window.localStorage['savedData']));
});
Edit: Now that you've provided the code for your service, it's clear that it doesn't return a promise. You need to do that in order for the consumers of your service to be able to wait on the results:
.service('FeedList', function ($rootScope, $q, FeedLoader) {
var feeds;
this.get = function() {
var feedSources = [
{title: 'rss1', url: 'http://www.website.com/rss/feed/rss_feed_25300'},
{title: 'rss2', url: 'http://www.website.com/rss/feed/rss_feed_10720'},
];
if (!feeds) {
var feedPromises = feedSources.map(function (source) {
return FeedLoader.fetch({q: source.url, num: 10}, {}).$promise
.then(function (data) {
return data.responseData.feed.entries;
});
});
feeds = $q.all(feedPromises)
.then(function (retrievedFeeds) {
return Array.prototype.concat([], retrievedFeeds);
});
}
return feeds;
};
})
The problem is that you don't handle async request properly, so:
$scope.feeds = [];
console.log('TEST : Load feeds async');
FeedList.get().then(function () { // each feed comes as argument
angular.forEach(arguments, function (feed) {
$scope.feeds.concat(feed); // merge arrays
});
window.localStorage.setItem('savedData', JSON.stringify($scope.feeds));
console.log('TEST : ' + window.localStorage['savedData']);
console.log('TEST : ' + JSON.parse(window.localStorage.getItem('savedData')));
console.log('TEST : ' + JSON.parse(window.localStorage['savedData']));
});
Service:
.service('FeedList', function ($q, $rootScope, FeedLoader) {
this.get = function () {
var promises = []; // to handle async loading
var feedSources = [{
title: 'rss1',
url: 'http://www.website.com/rss/feed/rss_feed_25300'
}, {
title: 'rss2',
url: 'http://www.website.com/rss/feed/rss_feed_10720'
}];
angular.forEach(feedSources, function (source) {
var defer = $q.defer();
FeedLoader.fetch({
q: source.url,
num: 10
}, {}, function (data) {
var feed = data.responseData.feed;
defer.resolve(feed.entries); // todo - need to handle errors with 'defer.reject'
});
promises.push(defer.promise);
});
return $q.all(promises);
};
})

Categories