This is my first post to this site, so I apologize in advance of any lack of data or tags, etc. I've been using this site for years, and it always helped me, but now I'm truly lost, and I couldn't find an answer anywhere.
I have an application where I need to call a web service 10 times, each time with a different parameter. The return payload is complex, so I created a custom object to hold the data. I need data from all 10 calls before moving forward with my code, which led me to callback hell. I'm trying to use Promises to simplify this, but this is where I'm facing this weird issue. I'm able to replicate this issue with a simple class:
Custom Object (Person):
var person = {
firstName : String,
lastName : String,
age : Number
}
function Person() { //getters and setters
} module.exports = Person;
Function getToken (returns a specific token my web service calls need), here replaced with a simple string:
function getToken() {
return new Promise(function(resolve, reject) {
var x = "random token";
console.log('getting token');
setTimeout(function(){resolve(x)}, 200);
});
}
Function getAction: in my real app, it calls the web service. Here it just creates a random person with an ID as input:
function getAction(uuid) {
return new Promise(resolve => {
var newPerson = new Person();
newPerson.setFirstName("John " + uuid);
newPerson.setLastName("Doe");
newPerson.setAge(20);
console.log("---> Returning Person " + newPerson.getFirstName());
setTimeout(function(){resolve(newPerson)}, 300);
});
}
Function getActions: calls getAction for each input parameter. This function itself must return a Promise because there's another function waiting for all the data to be available before continuing.
function getActions() {
return new Promise(function(resolve, reject) {
getToken().then(async function(tokenret) {
var userIds = ["001", "002", "003", "004", "005", "006", "007", "008", "009", "010" ];
var myPromise = Promise.join;
myPromise(getAction(userIds[0]), getAction(userIds[1]), getAction(userIds[2]), function(personOne, personTwo, personThree) {
console.log("Person One: " + personOne.getFirstName());
console.log("Person Two: " + personTwo.getFirstName());
console.log("Person Three: " + personThree.getFirstName());
});
}).catch(function(rej) {console.log("Promise Failed! " + rej);});
});
}
The output for this execution is:
---> Returning Person John 001
---> Returning Person John 002
---> Returning Person John 003
Person One: John 003
Person Two: John 003
Person Three: John 003
We can see that the getAction function was executed in the right order, with the right parameters. But all 3 variables created in the getActions function have the value from the last execution.
I also tried this code:
const allPromises = userIds.map(userIds => getAction(userIds));
await Promise.all(allPromises).then(function(allResults) {
console.log("Received " + allResults.length + " records");
var thisPersonZero = allResults[0];
console.log("This person 0: " + thisPersonZero.getFirstName());
var thisPersonOne = allResults[1];
console.log("This person 1 " + + thisPersonOne.getFirstName());
var thisPersonTwo = allResults[2];
console.log("This person 2 " + + thisPersonTwo.getFirstName());
console.log("Recapping");
console.log("This person 0: " + thisPersonZero.getFirstName());
console.log("This person 1: " + thisPersonOne.getFirstName());
console.log("This person 2: " + thisPersonTwo.getFirstName());
});
And I got this output:
---> Returning Person John 001
---> Returning Person John 002
---> Returning Person John 003
---> Returning Person John 004
---> Returning Person John 005
---> Returning Person John 006
---> Returning Person John 007
---> Returning Person John 008
---> Returning Person John 009
---> Returning Person John 010
Received 10 records
This person 0: John 010
This person 1 John 010
This person 2 John 010
Recapping
This person 0: John 010
This person 1: John 010
This person 2: John 010
Finally, I tried using await, which generated even weirder results:
var firstPerson = await getAction(userIds[0]);
console.log("First Person: " + firstPerson.getFirstName());
var secondPerson = await getAction(userIds[1]);
console.log("Second Person: " + secondPerson.getFirstName());
console.log("Recapping");
console.log("First Person: " + firstPerson.getFirstName());
console.log("Second Person: " + secondPerson.getFirstName());
Result:
---> Returning Person John 001
First Person: John 001
---> Returning Person John 002
Second Person: John 002
Recapping
First Person: John 002
Second Person: John 002
So the value is correct, until the callback for the next Promise, which replaces the value for all the variables. The behavior is the same if I create copies of the variable, even using JSON.parse(JSON.stringify()).
This code works perfectly if I use strings instead of the Person object. However it would be extremely cumbersome to try and do this without custom objects.
I'm sure that I am making some very basic mistake, but even though this seems to be very straightforward, I couldn't find anything on this particular issue anywhere. This issue is happening with Node versions 9.5 and 10, running on MacOS (if it makes any difference).
Any help will be very much appreciated. Thanks in advance!
Full code snippet:
// Person.js
var person = {
firstName : String,
lastName : String,
age : Number
}
function Person() {
Person.prototype.setFirstName = function(firstName) { person.firstName = firstName; }
Person.prototype.setLastName = function(lastName) { person.lastName = lastName; }
Person.prototype.setAge = function(age) { person.age = age; }
Person.prototype.getFirstName = function() { return (typeof person.firstName === 'undefined') ? '' : person.firstName; }
Person.prototype.getLastName = function() { return (typeof person.lastName === 'undefined') ? '' : person.lastName; }
Person.prototype.getAge = function() { return (typeof person.age === 'undefined') ? 0 : person.age; }
}
module.exports = Person;
// Error.js
var Promise = require('bluebird');
var Person = require("./models/Person");
function getToken() {
return new Promise(function(resolve, reject) {
var x = "random token";
console.log('getting token');
setTimeout(function(){resolve(x)}, 200);
});
}
function getActions() {
return new Promise(function(resolve, reject) {
getToken().then(async function(tokenret) {
var userIds = ["001", "002", "003", "004", "005", "006", "007", "008", "009", "010" ];
/*
var myPromise = Promise.join;
myPromise(getAction(userIds[0]), getAction(userIds[1]), getAction(userIds[2]), function(personOne, personTwo, personThree) {
console.log("Person One: " + personOne.getFirstName());
console.log("Person Two: " + personTwo.getFirstName());
console.log("Person Three: " + personThree.getFirstName());
});
*/
var firstPerson = await getAction(userIds[0]);
console.log("First Person: " + firstPerson.getFirstName());
var secondPerson = await getAction(userIds[1]);
console.log("Second Person: " + secondPerson.getFirstName());
console.log("Recapping");
console.log("First Person: " + firstPerson.getFirstName());
console.log("Second Person: " + secondPerson.getFirstName());
/*
const allPromises = userIds.map(userIds => getAction(userIds));
await Promise.all(allPromises).then(function(allResults) {
for (var x = 0; x < allResults.length; x++)
{
var thisPerson = allResults[x];
console.log("This Person: " + thisPerson.getFirstName());
}
console.log("Received " + allResults.length + " records");
var thisPersonZero = allResults[0];
console.log("This person 0: " + thisPersonZero.getFirstName());
var thisPersonOne = allResults[1];
console.log("This person 1 " + thisPersonOne.getFirstName());
var thisPersonTwo = allResults[2];
console.log("This person 2 " + thisPersonTwo.getFirstName());
console.log("Recapping");
console.log("This person 0: " + thisPersonZero.getFirstName());
console.log("This person 1: " + thisPersonOne.getFirstName());
console.log("This person 2: " + thisPersonTwo.getFirstName());
});
*/
}).catch(function(rej) {console.log("Promise Failed! " + rej);});
});
}
function getAction(uuid) {
return new Promise(resolve => {
var newPerson = new Person();
newPerson.setFirstName("John " + uuid);
newPerson.setLastName("Doe");
newPerson.setAge(20);
console.log("---> Returning Person " + newPerson.getFirstName());
setTimeout(function(){resolve(newPerson)}, 300);
});
}
getActions();
Without going too much into your code, I can already tell you that your Person 'class' methods are using person object declared at the top of the Person.js file. Fix those to use this and that should be it.
Person.prototype.setFirstName = function(firstName) { this.firstName = firstName; }
The way you have it set now the methods keep mutating the same object.
Related
Hi I have this code written and I want the output to be "bill jones is a snowboarder of beginner skill on the slopes.". I am getting "function () { return this.firstName + " " + this.lastName; } is a snowboarder of beginner skill on the slopes." I think the problem is the way I am calling the fullName method but I am stuck.
https://codepen.io/stefan927/pen/yLPJdQG?editors=1111
(function() {
const fullName = document.getElementById('fullName');
const type = document.getElementById('type');
const ability = document.getElementById('ability');
function Person(firstname, lastname, type, ability) {
this.firstName = firstname;
this.lastName = lastname;
this.type = type;
this.ability = ability;
}
Person.prototype.fullName = function() {
return this.firstName + " " + this.lastName;
}
var skier = new Person('Bill', 'Jones', 'snowboarder', 'beginner');
fullName.textContent = skier.fullName;
type.textContent = skier.type;
ability.textContent = skier.ability;
}())
Like Pointy pointed ( no pun intended ) out in his comment, you are not calling the fullName function which is invoked with a pair of (). So, in your code fullName.textContent = skier.fullName should be fullName.textContent = skier.fullName(). That should resolve your issue.
You have added fullName to the Persons prototype, and it's a function.
So you need to call/invoke this function.
fullName.textContent = skier.fullName(); //added () to fullName
Still can't understand my mistake with this code.
All what I want it - via prompt get all list of users (name / surname)
function UserList() {
let users = [];
while (true) {
let response = prompt('Please, enter your name surname?');
if (response == null) {
alert('cancel');
break;
}
users.push(response.split(' '));
}
return users;
}
function User() {
this.name = userList[0];
this.surname = userList[1];
this.regDate = new Date;
for (i = 0; i < userList.length; ++i) {
console.log('Name: ' + this.name + ' Surname: ' + this.surname + '. Date of registration : ' + this.regDate)
}
}
let userList = new UserList();
let user = new User();
And I faced with a misunderstanding why I cant get first word of prompt despite I put users.push (response.split(' ')).
userList [0] - shows first index of array instead first word.
And second I want to get all list of users in console.log but instead it I get the same string depending on length of array
userList[0] in the function User will return an array: ['name', 'surname'].
To get the first name for example, you need to use this.name = userList[i][0]
function UserList() {
let users = [];
while (true) {
let response = prompt('Please, enter your name surname?');
if (response == null) {
alert('cancel');
break;
}
users.push(response.split(' '));
}
return users;
}
function User() {
for (var i = 0; i < userList.length; ++i) {
this.name = userList[i][0];
this.surname = userList[i][1];
this.regDate = new Date;
console.log('Name: ' + this.name + ' Surname: ' + this.surname + '. Date of registration : ' + this.regDate)
}
}
let userList = new UserList();
let user = new User();
You are pushing an array in an other array, so your index is not correct (array looks like this: [["firstname", "lastname"]]). You could spread the items when pushing using the spread operator (...), you could also flatten the array using flat().
Also when creating a date, use new Date().
function UserList() {
let users = [];
while (true) {
let response = prompt('Please, enter your name surname?');
if (response == null) {
alert('cancel');
break;
}
users.push(...response.split(' ')); // flatten
}
return users;
}
function User() {
this.name = userList[0];
this.surname = userList[1];
this.regDate = new Date(); // ()
console.log('Name: ' + this.name + ' Surname: ' +
this.surname + '. Date of registration : ' + this.regDate)
}
let userList = new UserList();
let user = new User();
Using flat()
return users.flat();
Edit
I actually understood the question wrong (thought you only wanted 1 user), the other answer should be correct and makes more sense.
UserList shouldn't be a constructor. It should just be a function that returns an array of names.
You shouldn't be iterating over the list of users within User. You should then be iterating over the array creating one new User on each iteration which should be generated from a constructor. You can just pass in each name from the array and build an new object.
function getNames() {
const users = [];
while (true) {
const response = prompt('Please, enter your first and last names');
if (response == null) break;
users.push(response.split(' '));
}
return users;
}
// Pass in a user name from the array as an argument
// It's array so we can destructure the first and last name
// immediately
function User([first, last]) {
this.first = first;
this.last = last;
this.regDate = new Date();
this.message = `Name: ${this.first}. Surname: ${this.last}. Date of registration: ${this.regDate}.`;
}
// Iterate over the array generated by `getUsers`
// and for each name create a new user.
for (let name of getNames()) {
console.log(new User(name));
}
Additional documentation
Destructuring assignment
Apologies if this is a simple question, but is there a way to output Javascript to the console in real-time, while pausing at each user interaction. In other words, if I did something as simple as...
console.log("What's your name: ");
let userName = prompt("What is your name?");
console.log(userName);
console.log("\n");
console.log("Nice to meet you " + userName + "! What is your age?");
let userAge = prompt("What is your age?");
console.log(userAge);
...can I get the name outputted to the screen before the age prompt comes up? This is doable in other teaching environments (MS Basic & the command prompt), but either doesn't exist in Javascript, or I'm missing something.
Thanks for helping the newb!
You can make use of setTimeout, something like this:
console.log("What's your name: ");
let userName = prompt("What is your name?");
console.log(userName);
console.log("\n");
setTimeout(function(){
console.log("Nice to meet you " + userName + "! What is your age?");
let userAge = prompt("What is your age?");
console.log(userAge);
},1000)
Note: This is one way to do it there are ample of other ways to do it.
In case if you want to use promises:
let userName,userAge, userSport;
function basic(){
console.log("What's your name: ");
var promise1 = new Promise(function(resolve, reject){
userName = prompt("What is your name?");
resolve(userName)
})
promise1.then(function(value){
console.log(value);
console.log("\n");
console.log("Nice to meet you " + value + "! What is your age?");
var promise2 = new Promise(function(resolve, reject){
userAge = prompt("What is your age?");
resolve(userAge)
})
promise2.then(function(value){
console.log(value)
console.log("Nice to meet you " + userName + " aged: " +userAge + ". Let us know your favorite sports")
var promise3 = new Promise(function(resolve, reject){
userSport = prompt("What is your favorite sports?");
resolve(userSport)
})
promise3.then(function(value){
console.log(value)
console.log("Nice to meet you " + userName + " aged: " +userAge + " and your favorite sports is " +value)
console.log("\n");
console.log("Thanks");
})
})
})
}
basic()
You can use the async/await functionality and promises. I personally do not always like to use .then so this is my version of using Promises.
const Q1 = () => new Promise(resolve => {
let userName = prompt('What is your name?');
console.log(`What is your name: ${userName}`);
resolve(userName);
})
const Q2 = (userName) => new Promise(resolve => {
console.log(`Nice to meet you ${userName}! What is your age?`);
let age = prompt('What is your age?');
console.log(age);
resolve(age);
})
const userPrompt = async () => {
let name = await Q1();
let age = await Q2(name); // assigned to variable should you want to use it in next question
}
userPrompt();
You can now just add additional questions as functions.
Also here is a link to a JSFiddle. If you run it and open the console you will see that the name is displayed before the age prompt is opened.
I would like to compare the names, if they match, then say we have similar names, else that we have different names. But this code gives output undefined while comparing names.
Could someone please help me out to understand/fix this problem?
I want to get following, for example:
We have different names, Bob and I.
function addPersonMethods(obj){
this.obj = obj;
}
addPersonMethods.prototype.greet = function(str){
return str + ", my name is "+ this.obj.name;
}
addPersonMethods.prototype.nameshake = function(othername){
this.othername = othername;
if (this.obj.name == this.othername){
console.log("We have the same name ," + this.obj.name + " and I! ");
}
else
console.log("We have different names ," + this.obj.name + " and I.");
}
var bob_def = { name: 'Bob', age: 21 };
var eve_def = { name: 'Eve', age: 21 };
var bob = new addPersonMethods(bob_def);
var eve = new addPersonMethods(eve_def);
var another_bob = new addPersonMethods({name:'Bob', age: 40});
console.log(bob.greet("Hi all"));
console.log(eve.greet("Hello"));
console.log(another_bob.greet("Hey"));
console.log(bob.nameshake(eve));
Your nameshake() method expects a string (the name), but you're passing an object, so the comparison will never be true. You want to compare to that object's .obj.name.
Second, you're logging the result of bob.nameshake(), when the function doesn't actually return anything.
And you're printing your own name in the "We have..." statements, when you want to print the other person's.
function addPersonMethods(obj) {
this.obj = obj;
}
addPersonMethods.prototype.greet = function(str) {
return str + ", my name is " + this.obj.name;
}
addPersonMethods.prototype.nameshake = function(otherperson) {
var othername = otherperson.obj.name;
if (this.obj.name == othername) {
console.log("We have the same name, " + othername + " and I! ");
}
else
console.log("We have different names, " + othername + " and I.");
}
var bob_def = {
name: 'Bob',
age: 21
};
var eve_def = {
name: 'Eve',
age: 21
};
var bob = new addPersonMethods(bob_def);
var eve = new addPersonMethods(eve_def);
var another_bob = new addPersonMethods({
name: 'Bob',
age: 40
});
console.log(bob.greet("Hi all"));
console.log(eve.greet("Hello"));
console.log(another_bob.greet("Hey"));
bob.nameshake(eve);
bob.nameshake(another_bob);
You are comparing a string (this.obj.name) to an object (othername). They won't be equal so you will always output that they have different names. The return value of any function by default is undefined unless you specify otherwise, so that's why your output is tailed by undefined.
Either pass in eve.obj.name to the function, or extract that value inside the function so you can compare properly.
$(document).ready(function(){
function Employee(firstName) {
this.firstName = firstName;
}
Employee.prototype.goToWork = function(){
console.log("I go to office daily!");
};
Employee.prototype.sayHello = function(){
console.log("Hello, I'm " + this.firstName);
};
function Developer(firstName,project) {
Employee.call(this, firstName);
this.project = project;
};
Developer.prototype = Object.create(Employee.prototype);
Developer.prototype.constructor = Developer;
Developer.prototype.sayHello = function(){ //overrides sayHello()
console.log("Hello, I'm developer " + this.firstName + ". I'm in "
+ this.project + "project.");
};
Developer.prototype.developCode = function(){
console.log("I am developing code.");
};
$(".developerOK").click(function(){
var name=$(".developerName").val();
var project=$(".developerProject").val();
var Developer1 = new Developer(name,project);
Developer1.sayHello();
Developer1.goToWork();
Developer1.developCode();
});
function Manager(firstName,project) {
setTimeout(Manager.call(this,firstName),5);
this.project = project;
};
Manager.prototype = Object.create(Employee.prototype);
Manager.prototype.constructor = Manager;
Manager.prototype.sayHello = function(){
console.log("Hello, I'm manager " + this.firstName + ". I'm in "
+ this.project + "project.");
};
Manager.prototype.manageTeam = function(){
console.log("I manage team.");
};
$(".managerOK").click(function(){
var name=$(".managerName").val();
var project=$(".managerProject").val();
var Manager1 = new Manager(name,project);
Manager1.sayHello();
Manager1.goToWork();
Manager1.manageTeam();
});
});
When i click on managerOK button , it gives uncaught range error on manager.call()
I tried using setTimeout() but as my constructor is parametrized, I am not able to call it without parantheses.
Please help.
I think I see your problem. You are calling the Manager constructor asynchronously within itself, causing a stack overflow:
function Manager(firstName,project) {
setTimeout(Manager.call(this,firstName),5);
this.project = project;
};
This is a common problem and I've seen several variations of it. The manager is supposed to be assigned something 5 milliseconds later. Could it be one of your other types - Employee or Developer?
Hope that helps!