Javascript call function according to user input - javascript

I want to call a unique function according to the user's input on JavaScript. I'm working with Node.js, not HTML inputs. I'll leave an example code that explains my general code's goal. Is there any better approach than I did? What would you recommend to me? I don't want to add all the new functions to Map. I want to turn into dynamic too.
let functions = new Map([
["Annually", annually],
["Daily", daily],
["Weekly", weekly],
]);
async function choice() {
const answer = await inquirer.prompt({
name: "choice",
type: "list",
message: "How often do you want to get a notification?",
choices: ["Annually", "Daily", "Weekly"], //Gets choice from users think it's like a HTML form
});
functions.get(answer.choice)(); // Gets the function from map after that calls the function
}
async function annually() {
console.log("Annually works.");
}
async function weekly() {
console.log("Weekly works.");
}
async function daily() {
console.log("Daily works.");
}

i think this solution is better
const funcs = {
annually: async () => { // you can change "annually" to anything
console.log('Annually works.')
},
weekly: async () => {
console.log('Weekly works.')
},
daily: async () => {
console.log('Daily works.')
},
}
async function choice() {
const answer = await inquirer.prompt({
name: 'choice',
type: 'list',
message: 'How often do you want to get a notification?',
choices: Object.keys(funcs),
})
await funcs[answer.choice]()
}

The way you are accomplishing this seems like it would work, but I think there is a better way to do this that is more readable.
function handleDaily(){
console.log("daily works");
}
function handleWeekly(){
console.log("weekly works");
}
function handleAnnually(){
console.log("annually works");
}
var CHOICE_LIST = [];
const CHOICES = [
{value: "Daily", handle: handleDaily},
{value: "Weekly", handle: handleWeekly},
{value: "Annually", handle: handleAnnually},
];
//Builds choice list based off of CHOICES array
for(let i = 0; i < CHOICES.length; i++){
CHOICE_LIST.push(CHOICES[i].value);
}
function evalInput(input) {
for(let i = 0; i < CHOICES.length; i++){
if(input === CHOICES[i].value){
CHOICES[i].handle();
break;
}
}
}
async function getChoice(){
const answer = await inquirer.prompt({
name: "choice",
type: "list",
message: "How often do you want to get a notification?",
choices: CHOICE_LIST
});
evalInput(answer.choice);
}
No need to store the functions in a map unless you really need to. I also removed the async keyword from your functions that were not awaiting anything.

If these functions are in global object's scope you can do this:
var fn = answer.choice.toString();
if (global[fn] === "function") {
global[fn]();
}
If its in a module and you know the module exports
var someExports = require('./someExportsFile.js');
Object.keys(someExports)[fn];
PS:
If this was in browser, you could have used
var brFn = window[fn)];
if (typeof brFn === "function") brFn();

Related

Follow on actions after asynch function completion

I'm going to do my best to explain this one since this is a reduced version of the real code and some parts might seem redundant or excessive (and they might be). The model can be found at https://jsfiddle.net/ux5t7ykm/1/. I am using a function to build an array of values that are passed to another function to do a REST call for the data that corresponds to the values. The array lists the name of the list to get the data from, the field name, the filter field name, the filter value, and the target array to pass the autocomplete values back to. This part is working fine and is essentially what Step1() does. setAutocomplete() then uses getData() to make the multiple REST calls. I'm using sprLib for that part so it's already a promise (this is getListItems() normally). getData() is where the magic is happening. It recursively cycles through the list and performs getListItems() until all the data is retrieved. I have this set as an async function with await. This also seems to be running fine.
The problem I have is that once this whole sequence is complete, I then have another function that I need to run. It manipulates the data from the previous function. I've tried to put step1() in a promise, make it async, etc. Nothing seems to work. CheatStep() seems to be where a response or some other method signaling to Step1() should go to then allow the next function to run. I've been banging my head against this all week and I feel like my understanding of promises and asnyc is regressing.
var dict = [{
'id': '1',
'title': 'test1'
}, {
'id': '2',
'title': 'test2'
}, {
'id': '3',
'title': 'test3'
}, {
'id': '4',
'title': 'test4'
}, ]
step1()
nextStep()
function nextStep() {
console.log('I run after Step1')
}
function cheatStep() {
console.log('I cheated')
}
function step1() {
setAutoComplete(4);
}
function setAutoComplete(listIndex) {
getData(listIndex,
function(result) {
for (var i = 0; i < listIndex; i++) {
var autoDict = result[i];
console.log(autoDict)
}
cheatStep()
},
function(e) {
console.error(e);
alert('An error occurred wile setting autocomplete data for item');
})
}
async function getData(listIndex, success, error, curIndex, result) {
var curIndex = curIndex || 0;
var result = result || {};
await getListItems(curIndex,
function(listItems) {
result[curIndex] = listItems;
curIndex++;
if (listIndex > curIndex) {
getData(listIndex, success, error, curIndex, result);
console.log('1');
} else {
console.log('2');
success(result);
}
},
error)
}
function getListItems(curIndex, success, error) {
new Promise(
function(resolve, reject) {
var x = dict[curIndex];
resolve(x);
})
.then(function(x) {
success(x);
});
}
I have adopted your code to a more async/await view coz you still use heavily callback approach:
var dict = [{
'id': '1',
'title': 'test1'
}, {
'id': '2',
'title': 'test2'
}, {
'id': '3',
'title': 'test3'
}, {
'id': '4',
'title': 'test4'
}, ]
function nextStep(result) {
console.log('I run after Step1',result)
}
function cheatStep() {
console.log('I cheated')
}
step1().then((result)=>{nextStep(result)})
async function step1() {
return await setAutoComplete(4);
}
async function setAutoComplete(listIndex) {
const result = await getData(listIndex);
for (var i = 0; i < listIndex; i++) {
var autoDict = result[i];
console.log(autoDict)
}
cheatStep()
return result
}
async function getData(listIndex, curIndex=0, result={}) {
const listItems = await getListItems(curIndex)
result[curIndex] = listItems;
curIndex++;
if (listIndex > curIndex) {
return await getData(listIndex, curIndex, result);
} else {
console.log('2');
return (result);
}
}
function getListItems(curIndex) {
return new Promise(
(resolve, reject) =>{
resolve(dict[curIndex]);
})
}
I also kept recursion here, but personally I would prefer solution with a loop instead.
Also a minor advise. Try to avoid write declaration after usage:
nextStep()
function nextStep() {
console.log('I run after Step1')
}
JS hoisting does that a valid syntax but it is really hard to read=)

Infinite functions calls like 'string'.replace().replace()

I'm not really sure how to explain so I will start with the output.
I need to return this:
{
replies:
[
{ type: 'text', content: 'one' }
{ type: 'text', content: 'two' }
{ type: 'text', content: 'three' }
],
conversation: {
memory
}
}
And I wanted to return that through in-line statement.
So I would like to call something like:
reply.addText('one').addText('two').addText('three').addConversation(memory)
Note that addText can be called infinite times while addConversation can be called only one time. Also conversation is optional, in that case, if conversation is absent the conversation object should not appear in the output.
To create a custom structured object use a constructor, say Reply.
To call instance methods on the return value of method calls, return the instance object from the method.
Choices to prevent multiple additions of conversation objects include throwing an error (as below) or perhaps logging a warning and simply not add additional objects after a first call to addConversation.
Write the code to implement the requirements.
For example using vanilla javascript:
function Reply() {
this.replies = [];
}
Reply.prototype.addText = function( content) {
this.replies.push( {type: "text", content: content});
return this;
}
Reply.prototype.addConversation = function( value) {
if( this.conversation) {
//throw new Error("Only one conversation allowed");
}
this.conversation = {conversation: value};
return this;
};
Reply.prototype.conversation = null;
// demo
var reply = new Reply();
reply.addText( "one").addText("two").addConversation("memory?");
console.log( JSON.stringify( reply, undefined," "));
(The console.log uses JSON stringify to avoid listing inherited methods)
A possible implementation is to create a builder as follows:
function create() {
const replies = []; // store all replies in this array
let conversation; // store the memory here
let hasAddConversationBeenCalled = false; // a state to check if addConversation was ever called
this.addText = function(content) {
// add a new reply to the array
replies.push({
type: 'text',
content
});
return this; // return the builder
};
this.addConversation = function(memory) {
if (!hasAddConversationBeenCalled) { // check if this was called before
// if not set the memory
conversation = {
memory
};
hasAddConversationBeenCalled = true; // set that the memory has been set
}
return this; // return the builder
}
this.build = function() {
const reply = {
replies
};
if (conversation) { // only if converstation was set
reply.conversation = conversation; // add it to the final reply object
}
return reply; // finally return the built respnse
}
return this; // return the new builder
}
You can then use it as follows:
const builder = create();
const reply = builder.addText('one').addText('two').addText('three').addConversation({}).build();
Here is a link to a codepen to play around with.
If you specifically want to add assemble this via multiple function calls, then the builder pattern is your best bet, as vader said in their comment.
However, if the goal is to simply create shorthand for concisely building these objects, it can be done using a function that takes the list of text as an array.
const buildObject = (textArray, memory) => {
return Object.assign(
{},
{
replies: textArray.map(x => {
return {
type: 'text',
value: x
}
})
},
memory ? {conversation: memory} : null
)
}
var memory = { };
//with memory
console.log(buildObject(['one', 'two', 'three'], memory ))
//without memory
console.log(buildObject(['one', 'two', 'three']));
Fiddle example: http://jsfiddle.net/ucxkd4g3/

Sequelize nodejs pass value to another function

Here is my function which return count fine.But I want its value to be used in my response.
var PostComment = require("../models/PostComment");
var getPostCount = function (postId,res) {
return PostComment.findAll({where: {post_id: postId}}).then(function (comments) {
return comments.length;
});
}
module.exports = {
getPostCount: getPostCount
}
Here is my another function. Console log working fine. But I need to return value.
arr.push({
id: item.id,
post_userid: item.post_userid,
post_sendername: item.post_sendername,
post_description: item.post_description,
attach_status: item.attach_status,
post_datetime: item.post_datetime,
post_status: item.post_status,
remove_id: item.remove_id,
attachment_list: item.tbl_post_attachments,
total_like:totalLikes,
comment_count:Samparq.getPostCount(item.id, res).then(function (commentCount) {
console.log(commentCount);
}),
comment_list:item.tbl_post_comments
});
SIDENOTE:
You can use the count method in your first code block:
var PostComment = require("../models/PostComment");
var getPostCount = function (postId) {
return PostComment.count({where: {post_id: postId}});
}
module.exports = {
getPostCount: getPostCount
}
In your second code block, you're getting a Promise back in comment_count, not an actual number.
You need to first get the count, then add it to the object.
Samparq.getPostCount(item.id).then(function (count) {
arr.push({
id: item.id,
post_userid: item.post_userid,
post_sendername: item.post_sendername,
post_description: item.post_description,
attach_status: item.attach_status,
post_datetime: item.post_datetime,
post_status: item.post_status,
remove_id: item.remove_id,
attachment_list: item.tbl_post_attachments,
total_like:totalLikes,
comment_count: count,
comment_list:item.tbl_post_comments
});
})
SIDENOTE #2:
If you use async await, which is new to node, you'll be able to make it look more like your original code. If you're inside an async function, then your second codeblock could look like this:
arr.push({
id: item.id,
post_userid: item.post_userid,
post_sendername: item.post_sendername,
post_description: item.post_description,
attach_status: item.attach_status,
post_datetime: item.post_datetime,
post_status: item.post_status,
remove_id: item.remove_id,
attachment_list: item.tbl_post_attachments,
total_like:totalLikes,
comment_count: await Samparq.getPostCount(item.id),
comment_list:item.tbl_post_comments
});

How to chain JavaScript methods natively from an iterator?

Say I'm using js methods that have the 'this' returned, so one can chain like such:
something.add({a: 'xyz', b: 123}).add({a: 'abc', b: 456});
How can I chain these on something from an iterator? For example:
$scope.bindings = [
{
key: 'up',
func: function() {
$scope.scroll(1)
}
},{
key: 'down',
func: function() {
$scope.scroll(-1);
}
},
];
---EDIT---
The library I was using is Angular hotkeys. I wanted to ask in a generic way to help anyone else in a similar position.
I have:
var hotBindings = hotkeys.bindTo(scope);
for (var bind of scope.bindings) {
hotBindings = hotBindings.add({
combo: bind.key,
callback: function(e) {
e.preventDefault();
bind.func();
}
})
}
This assigns the 'down' method to both keypresses. If I write out the code without the loop, using scope.bindings[index].key (for example) and chain the .add() method then it works. I also tried without "hotBindings =".
Please don't mention the scopve vs $scope as this is being passed into a link function in a angular directive - angular almost certainly has nothing to do with it.
The only problem I see is the not working for (var bind of bindings).
Edit: thought it had something to do with the for (var bind of bindings) syntax and var v.s. let, turns out the provided code just works. I'll delete this answer if the real problem surfaces. Must be in the Something class?
Everything seems to work:
var Something = function() {
this.data = [];
this.add = function(item) {
this.data.push(item);
return this;
}.bind(this);
};
var bindings = [{
x: 'hi',
func: function() {
console.log("hi");
}
}, {
x: 'bye',
func: function() {
console.log("bye");
}
}];
var something = new Something();
for (var bind of bindings) {
something.add({
x: bind.x,
callback: bind.func
})
};
console.log(something.data);
I'm not sure what SomeThing is or what it's add method returns, but you can replicate the chaining by doing
let something = new Something();
for (const bind of bindings) {
something = something.add({
x: bind.x,
callback: bind.func
});
}
If you do this a lot (perhaps using different methods than just "add"), you might consider using a helper function:
function chain(obj, ...calls) {
for(let {method, args} of calls) {
obj = obj[method](...args)
}
return obj
}
chain(new Somthing(), [
{meth: 'add', args: [
{x: 'hi', func: () => console.log('hi')}]},
{meth: 'add', args: [
{x: 'bye', func: () => console.log('bye')}]}
]})

jQuery asynchronous issue undefined value

I am having difficulties with Javascript's asynchronous functions.
Code displayed below is so far almost entire code I have and I can't get it to work.
I ma trying to use Eventful API to pull some data from the server, and display it in my frontend which is created by jQuery.
So, the problem is following: function search, which is calling function Eventful.prototype.searchanje always ends up with undefined value, while a few seconds later, function searchanje console logs actual/recieved data.
I am fairly new to jQuery, and I was wondering if there is any kind of "template" for handling these things, concretely for waiting until the function returns value and then proceeding with next operations.
So far I have tried using deferred and promises, and read quite a lot tutorials and stackoverflow answers on similar subjects, but I can't get it to work properly.
Or, if deferred and promises are the right way to go, could you show me the way it is supposed to be done?
Thanks in advance
'use strict';
function Eventful(_name) {
var name = _name;
var appKey = 'appKey';
var that = this;
return {
getName: function() {
return name;
},
getAppKey: function() {
return appKey;
},
search: function() {
that.searchanje(appKey).then(function(oData) {
console.log('oData');
});
}
};
}
Eventful.prototype.searchanje = function(appKey) {
var oArgs = {
app_key: appKey,
q: 'sport',
where: 'Zagreb',
date: '2013061000-2015062000',
page_size: 5,
sort_order: 'popularity',
};
EVDB.API.call('/events/search', oArgs, function(oData) {
console.log(oData);
return oData();
});
};
On the line
EVDB.API.call('/events/search', oArgs, function(oData) {
you are passing a CALLBACK function. This function (the one that starts function(oData)) is not executed immediately. It is executed asynchronously, when the result of your API call is returned.
Try putting console.log() statements around your code, and watch the order they appear in the console. For example:
function Eventful(_name) {
var name = _name;
var appKey = 'appKey';
var that = this;
return {
getName: function() {
return name;
},
getAppKey: function() {
return appKey;
},
search: function() {
console.log('Search function called');
that.searchanje(appKey).then(function(oData) {
console.log('searchanje returned with data:');
console.log('oData');
});
}
};
}
Eventful.prototype.searchanje = function(appKey) {
console.log('function searchanje being called with appKey: ', appKey);
var oArgs = {
app_key: appKey,
q: 'sport',
where: 'Zagreb',
date: '2013061000-2015062000',
page_size: 5,
sort_order: 'popularity',
};
console.log('Calling EVDB.API.call');
EVDB.API.call('/events/search', oArgs, function(oData) {
console.log('EVDB.API callback executing with data:');
console.log(oData);
return oData();
});
console.log('finished calling EVDB.API.call');
};
I wonder if a better way ahead might be to "promisify" EVDB.API.call().
(function(app_key) {
var appKeyObj = { 'app_key': app_key },
EVDB.API.callAsync = function(path, params) {
return $.Deferred(function(dfrd) {
EVDB.API.call(path, $.extend(appKeyObj, params), function(oData) {
if (oData.error === "1") {
//translate oData.status and oData.description into a javascript Error object
// with standard .name and .message properties.
err = new Error(oData.description);
err.name = oData.status;
dfrd.reject(err);
} else {
dfrd.resolve(oData);
}
});
});
});
})('myAppKey'); //hard-coded app key
Notes:
The global namespace is avoided by :
using a self-executing anonymous wrapper
extending the EVDB.API namespace.
The "Async" suffix for a promisified method has a precendent in bluebird
From the little I understand of EVDB.API, EVDB.API.call() does just about everything, hence the single method EVDB.API.callAsync(). If necessary, further async methods could be added to EVDB.API
You might choose to use a dedicated promise lib such as bluebird or when.js in place of jQuery, In addition to including the lib, mods to the above code would be minor.
Now, instead of calling EVDB.API.call(path, params, callback) you would call EVDB.API.callAsync(path, params) to be returned a Promise.
var params = {
//app_key will be inserted automatically
q: 'sport',
where: 'Zagreb',
date: '2013061000-2015062000',
page_size: 5,
sort_order: 'popularity'
};
EVDB.API.callAsync('/events/search', params).then(function(oData) {
console.log(oData);
}, function(err) {
console.error(err);
});

Categories

Resources