I need to iterate over result set from the sequelize result. I have a code that works, but I think something is wrong with it, and it should not be done this way.
I have a feeling this is a blocking code.
This is the code that works:
models.Project.findAll({
where: {ProjectId: projectId}
})
.then(function (projects) {
//Iteration is here
var projectList = [];
projects.forEach(function (res) {
projectList.push(res.dataValues.PartId);
});
//then bulk lookup for the result
models.Customers.findAll({
where: {'id': {in: [projectList]}}
}).then(function (customers) {
reply(customers).code(200);
}, function (rejectedPromiseError) {
reply(rejectedPromiseError).code(401);
});
//reply(sameparts).code(200);
}, function (rejectedPromiseError) {
reply(rejectedPromiseError).code(401);
});
This iteration parts is done:
projects.forEach(function (res) {
projectList.push(res.dataValues.PartId);
});
And then this code is executed as another query:
models.Customers.findAll({
where: {'id': {in: [projectList]}}
}).then(function (customers) {
reply(customers).code(200);
}, function (rejectedPromiseError) {
reply(rejectedPromiseError).code(401);
});
How can I rearange it so it uses Promises?
EDIT (Possible solution):
After playing a bit, I believe I have implemented promises.
getAll: function (request, reply) {
var projectId = request.params.projectid;
var promises = [];
var post;
models.SamePart.findAll({
where: {ProjectId: projectId}
})
.then(function (sameparts) {
//Iteration is here
sameparts.forEach(function (res) {
promises.push(
Promise.all([
models.Parts.findAll({where: {id: res.dataValues.PartId}})
]))
});
//Bulk lookup for the parts that were marked as identical
return Promise.all(promises);
}).then(function (completepartslist) {
reply(completepartslist).code(200);
});
Is this a correct approach? It seems that completepartslist contains many unwanted objects, including Promise stuff. How can I flatten it, so to avoid complex for loops?
If you are using .then(), then you are, in all probability, already using promises.
Your original, working code doesn't appear to be blocking.
Your final getAll() looks like it should simplify to :
getAll: function (request, reply) {
models.SamePart.findAll({
where: { ProjectId: request.params.projectid }
}).then(function (sameparts) {
return Promise.all(sameparts.map(function (res) {
return models.Parts.findAll({ where: { id: res.dataValues.PartId } });
}));
}).then(function (completepartslist) {
reply(completepartslist).code(200);
});
}
However, you need to add error handling back in.
even more simplified
getAll: function (request, reply) {
models.SamePart.findAll({
where: { ProjectId: request.params.projectid }
}).reduce(function (completepartslist, sameparts) {
return models.Parts.findAll({ where: { id: sameparts.PartId } }).
then(function(res){
completepartslist.concat(res)
});
}), []);
}).then(function (completepartslist) {
reply(completepartslist).code(200);
});
}
Related
I am looking to assign as an object a Fetch API promise from a local GeoJSON file.
Here is the code
fetch("data/sites.geojson")
.then(function(response) {
return response.json();
})
.then(function(data) {
L.geoJSON(data, {
pointToLayer: styles_sites
}).addTo(map);
});
};
I tried the call back method, as advised here
Saving fetched JSON into variable
(EDIT) New code, but there is still a missing formal parameter
function getData("data/sites.geojson", cb) {
fetch("data/sites.geojson")
.then(function(response) {
return response.json();
})
.then(function(data) {
L.geoJSON(data, {
pointToLayer: styles_sites,
onEachFeature: function (feature, layer) {
layer.on('mouseover', function() {
layer.openPopup(layer.bindPopup("<b>"+feature.properties.nombre+"</b>"))
});
layer.on('mouseout', function() {
layer.closePopup();
});
layer.on('click', function () {
layer.bindPopup("<b>Nombre: </b>"+feature.properties.nombre+"<br><b>Barrio: </b>"+feature.properties.barrio+"<br><b>Tipo: </b>"+feature.properties.tipo+"<br><b>Ubicacion: </b>"+feature.properties.ubicacion+"<br><b>Correo: </b>"+feature.properties.contacto);
});
}
}).addTo(map);
.then(function(result) {
cb(result);
});
});
};
getData("data/sites.geojson", function (data) {
return console.log({data});
});
Most probably just incorrect syntax of your callback function:
// Use either arrow function
getData("data/sites.geojson", (data) => {
return console.log({data});
});
// or standard function
getData("data/sites.geojson", function (data) {
return console.log({data});
});
I found the way to work this out by adding within the fetch function, what I originally wanted to do on the map.
This was to add a L.controlLayer using the geojson as overlay.
This is the code that made it work:
let sites = getData()
.then((function(data) {
L.geoJSON(data, {
pointToLayer: styles_sites,
onEachFeature: function LayerControl(feature, layer) {
var popupText = "<b>" + feature.properties.nombre + "<br>";
layer.bindPopup(popupText);
category = feature.properties.tipo;
// Initialize the category array if not already set.
if (typeof categories[category] === "undefined") {
categories[category] = L.layerGroup().addTo(map);
layersControl.addOverlay(categories[category], category);
}
categories[category].addLayer(layer);
layer.on('mouseover', function() {
layer.openPopup(layer.bindPopup("<b>"+feature.properties.nombre+"</b>"))
});
layer.on('mouseout', function() {
layer.closePopup();
});
layer.on('click', function () {
layer.bindPopup("<b>Nombre: </b>"+feature.properties.nombre+"<br><b>Barrio: </b>"+feature.properties.barrio+"<br><b>Tipo: </b>"+feature.properties.tipo+"<br><b>Ubicacion: </b>"+feature.properties.ubicacion+"<br><b>Correo: </b>"+feature.properties.contacto);
});
}
}).addTo(map);
}));
Actually it comes from one of your answer on another post ghybs.
I have a computed property that will only be used if a match for a property exists. Because of this, I'm making the call to get the data asynchronous so that it's only retrieved when needed. I'm having an issue though trying to make an async call to return data for a computed property.
Below is what I have:
new Vue({
el: "#formCompleteContainer",
data: {
form: {},
components: []
},
computed: {
employeeList: function () {
var self = this;
if (_.some(this.components, function (component) {
return component.ComponentInfo.Type === 8
})) {
var employees = [];
$.ajax({
url: "/Form/GetAllUsers",
type: "GET"
}).done(function (results) {
employees = results;
});
return employees;
} else {
return [];
}
}
}
});
I know this isn't working because I'm returning before the call is complete. I've seen how to use deferredobjects and what not but I can't seem to figure out how to implement it with Vue.
For your use case, I don't think computed property can implement the goal.
My solution:
create one data property as one 'defered' object,
then uses one watch to async call your backend to get new data, finally assign to the defered object
like below demo:
Vue.config.productionTip = false
app = new Vue({
el: "#app",
data: {
product: "Boots",
deferedProduct: ''
},
watch: {
product: function (newVal, oldVal) {
setTimeout(() => {
this.deferedProduct = 'Cats in ' + newVal + '!'
}, 1500)
}
},
methods: {
nextProduct: function () {
this.product += 'a'
}
}
})
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<button #click="nextProduct()">Click Me!</button>
<h2>{{product}}</h2>
<h2>{{deferedProduct}}</h2>
</div>
This is what vue-async-computed is meant for. It resolves the promise you returned and handles any race conditions.
new Vue({
el: "#formCompleteContainer",
data: {
form: {},
components: []
},
asyncComputed: {
employeeList: function () {
if (_.some(this.components, function (component) {
return component.ComponentInfo.Type === 8
})) {
return $.ajax({
url: "/Form/GetAllUsers",
type: "GET"
});
} else {
return Promise.resolve([]);
}
}
}
});
After doing some more research I have gone another route. I agree with Sphinx that I don't think what I am trying to achieve will work with a computed property.
Instead, this is what I am going with:
new Vue({
el: "#formCompleteContainer",
data: {
form: {},
components: [],
employees: []
},
methods: {
getEmployees: function () {
var self = this;
if (_.some(this.components, function (component) {
return component.ComponentInfo.Type === 8;
})) {
$.ajax({
url: "/Form/Form/GetAllUsers",
type: "GET"
}).done(function (results) {
self.employees = results;
});
}
}
},
created: function () {
this.form = pageModel.Form;
this.components = pageModel.Components;
},
mounted: function () {
this.getEmployees();
}
});
As pointed out already, mounted and other 3rd party solutions can work.
However, better readability and component loading will come from putting the desired Promise within a data property. And then using the Vue lifecycle hook created, we can wait for that Promise to resolve with a .then.
For example:
requestService.js:
...
async foo(){
let myRequest = someRequest.createInstance()
await myRequest.onReady()
return myRequest.getSomePromise()
}
...
And then import the service into your component, as well as declaring a data prop:
myComponent.vue
...
data: (){
myPromiseLoc: null,
}
...
created: (){
requestService.foo().then( result =>
{
this.myPromiseLoc = result
}
}
...
I am learning React and I am trying to display a list of users from and ajax call. I am getting an unexpected token error from CodePen when I add the line
export default Users;
When I remove the line there are no more errors but the list of users is not being displayed.
My code:
function GetUsers(project){
$.ajax({
url: "https://jsonplaceholder.typicode.com/users",
success: function (data) {
console.log(data);
callback(null, data);
},
error: function (error) {
console.log(data);
callback(error, {});
}
});
}
function UserList(users) {
const userItems = users.map((user) =>
<ul>
<li>
{ user.name }
</li>
<li>
{ user.email }
</li>
<li>
{ user.phone}
</li>
</ul>
);
return (userItems);
}
class Users extends Component {
componentDidMount() {
GetUsers(null, function (err, data) {
if (err)
{
console.log(err);
}// do something
this.setState({ users: data })
}.bind(this))
}
render() {
return(
<UserList user = {this.state.users} />
);
}
}
if (document.getElementById('root')) {
ReactDOM.render(<Users />, document.getElementById('root'));
}
Here is my code.
Thank you for any and all help!
Problem 1 in AJAX call
function GetUsers(project){
$.ajax({
url: "https://jsonplaceholder.typicode.com/users",
success: function (data) {
console.log(data);
callback(null, data);
},
error: function (error) {
console.log(data);
callback(error, {});
}
});
}
$.ajax is asynchronous call, that means it doesn't returns directly any value (how it could if it is fetching the results from the internet) it Just creates another function which will call success and error when completed.
That's why we need to wrap it with callbacks
function GetUsers(project, resolve = () => {}, reject = () => {}) {
}
Problem 2 in mount
componentDidMount() {
GetUsers(null, function (err, data) {
if (err)
{
console.log(err);
}// do something
this.setState({ users: data })
}.bind(this))
}
This code is completely wrong, it has even syntax error so not worth to discuss it in details.
We need to call our new function and pass success callback for mutating the state
GetUsers(null, users => {
this.setState({ users });
});
In this way we will call GetUsers wait for it's results and only after that we will mutate the state with new result
3 problem in component creation
React component's don't have state by default, you need to infer the state from constructor so we need to change initialization to
constructor(props) {
super(props);
this.state = {
users: false
};
}
otherwise you will get Cannot call setState of undefined as state is not automatically created for performance purposes and all components are Pure by default.
I have created a working sandbox here
in
function GetUsers(project){
$.ajax({
url: "https://jsonplaceholder.typicode.com/users",
success: function (data) {
return data;
},
error: function (error) {
console.log(error);
return {};
}
});
}
--
success: function (data) {
return data;
}
doesn't do what you think it does. return data isn't really returning the data... anywhere.
You need to have a callback.
function GetUsers(project, callback){
$.ajax({
url: "https://jsonplaceholder.typicode.com/users",
success: function (data) {
callback(null, data)
},
error: function (error) {
callback(error, {})
}
});
}
class Users extends Component {
componentDidMount() {
GetUsers(null, function (err, data) {
if (err) // do something
this.setState({ users: data })
}.bind(this))
}
render() {
return(
<UserList user = {this.state.users} />
);
}
}
you can also Promise-ify things to simplify the logic
How would I add this code:
var query = new Parse.Query("Obj1");
query.equalTo("user", Parse.User.current());
query.first().then(function(obj) {
to the front of the chain in 'perform()'. Currently 'perform()' makes the two Parse.Cloud.httpRequest calls. I'd like it to do the Parse query first.
module.exports = {
version: '1.0.0',
initialize: function() {
return this;
},
perform: function(options) {
return Parse.Cloud.httpRequest({
method:'POST',
url:'http://...',
}).then(function(httpResponse) {
return Parse.Cloud.httpRequest({
method:'POST',
url:'http://...',
}).then(function(httpResponse) {
return httpResponse;
});
}, function(httpResponse) {
if (options && options.error) {
options.error(httpResponse);
}
});
}
}
Your code already demonstrates you understand thenable chaining, you can just apply it here:
// inside your function
var query = new Parse.Query("Obj1");
query.equalTo("user", Parse.User.current());
query.first().then(function(obj) {
// use obj here
return Parse.Cloud.httpRequest({
method:'POST',
url:'http://...',
});
}).then(... // rest of your code
I'm using jasmine+karma to run the following code...
and get the following error:
Expected { then : Function, catch : Function, finally : Function } to equal 123.
Can someone help me understand why I don't get a resolved value for my promise. thanks
'use strict';
angular
.module('example', ['ui.router'])
.config(function($stateProvider) {
$stateProvider
.state('stateOne', {
url: '/stateOne',
resolve: {cb: function($q) {
var deferred = $q.defer();
deferred.resolve(123);
return deferred.promise;
}},
controller: function($scope, cb) {console.log(' * in controller', cb);},
templateUrl: 'stateOne.html'
});
})
.run(function($templateCache) {
$templateCache.put('stateOne.html', 'This is the content of the template');
});
describe('main tests', function() {
beforeEach(function() {module('example');});
describe('basic test', function($rootScope) {
it('stateOne', inject(function($rootScope, $state, $injector, $compile) {
var config = $state.get('stateOne');
expect(config.url).toEqual('/stateOne');
$compile('<div ui-view/>')($rootScope);
$rootScope.$digest();
expect($injector.invoke(config.resolve.cb)).toEqual(123);
}));
});
});
Ok, Figured it out with some help (via email) from Nikas, whose blog I found at:
http://nikas.praninskas.com/angular/2014/09/27/unit-testing-ui-router-configuration/.
Here is a succinct example that demonstrates how to test the resolve values in ui.router, where the values involve $http.
angular
.module('example', ['ui.router'])
.factory('Clipboard', function($http) {
return {
get: function(args) {
return $http.get('/db/clipboard');
}
};
})
.config(function($stateProvider) {
$stateProvider
.state('stateOne', {
resolve: {cb: function(Clipboard) {
return Clipboard.get();
}}
});
});
describe('main tests', function() {
beforeEach(function() {module('example');});
it('stateOne', inject(function($state, $injector, $httpBackend) {
$httpBackend.whenGET('/db/clipboard').respond({a:1});
$injector.invoke($state.get('stateOne').resolve['cb'])
.then(function(res) {console.log(' *res ', res.data);})
.catch(function(err) {console.log(' *err ', err);});
$httpBackend.flush();
}));
afterEach(inject(function($httpBackend) {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
}));
});