I have simple little program which defines two functions as variables. These functions both return RSVP Promises so that they can be chained together as I have done below. I use this pattern often but in this case I'm running into madness ...
var opChown = function() { ... };
var opChgrp = function() { ... };
debug('chown: ' + opChown);
debug('chgrp: ' + opChgrp);
// Chain permission operations
opChown()
.then(opChgrp())
.then(function() {
debug('resolving parent promise');
resolve();
})
.catch(function(err) {
debug('rejecting parent promise');
reject(err);
}
);
While my debug statements clearly show that the two functions are in fact defined, when I execute them I get an unhandled exception:
TypeError: undefined is not a function
Please help me losing my mind. Any and all suggestions welcome.
For some additional context here is the definition of opChown:
var opChown = function() {
return new RSVP.promise(function(resolve,reject) {
debug('opChown');
if(options.ignoreChown) {
resolve();
} else {
var chown = shell('chown',['-R', downgradedUser, destDir]);
debug('chown command executed; user permission is "%s"', downgradedUser);
chown.on('exit',function(code) {
if(code !== 0) {
var errMessage = 'Build [' + code + ']:' + ' problems changing ownership on "' + destDir + '" directory to ' + downgradedUser + '.';
debug('Problem with chown: %s', code);
reject({operation:'chown', message: errMessage});
} else {
console.log(' - %s executed, user permissions changed to %s', chalk.bold('chown'), chalk.bold(downgradedUser));
resolve();
}
}); // end chown
}
});
}; // end opChgOwn
Now based on #bergi's great pointer that the stacktrace is indeed available to me on Node's 'uncaughtException' event, here is the stacktrace which clearly points to the problem being within the opChown function rather than the function itself:
TypeError: undefined is not a function
at opChown (/lib/broccoli-watcher.js:117:13)
at ChildProcess.<anonymous> (/lib/broccoli-watcher.js:167:5)
at ChildProcess.emit (events.js:98:17)
at Process.ChildProcess._handle.onexit (child_process.js:809:12)
Although I had expected the error message to be something like undefined is not a constructor, it looks as if the line
return new RSVP.promise(function(resolve,reject) {
should rather be
return new RSVP.Promise(function(resolve,reject) {
// ^
Notice that #SLaks spotted a mistake as well: then takes callback functions. You probably want to use
opChown().then(opChgrp)…
.then(opChgrp())
You just called opChgrp immediately and passed its result to then().
Since it doesn't return anything, you get an error.
Related
My problem is that the code does not seem to be running in order, as seen below.
This code is for my discord.js bot that I am creating.
var Discord = require("discord.js");
var bot = new Discord.Client();
var yt = require("C:/Users/username/Documents/Coding/Discord/youtubetest.js");
var youtubetest = new yt();
var fs = require('fs');
var youtubedl = require('youtube-dl');
var prefix = "!";
var vidid;
var commands = {
play: {
name: "!play ",
fnc: "Gets a Youtube video matching given tags.",
process: function(msg, query) {
youtubetest.respond(query, msg);
var vidid = youtubetest.vidid;
console.log(typeof(vidid) + " + " + vidid);
console.log("3");
}
}
};
bot.on('ready', () => {
console.log('I am ready!');
});
bot.on("message", msg => {
if(!msg.content.startsWith(prefix) || msg.author.bot || (msg.author.id === bot.user.id)) return;
var cmdraw = msg.content.split(" ")[0].substring(1).toLowerCase();
var query = msg.content.split("!")[1];
var cmd = commands[cmdraw];
if (cmd) {
var res = cmd.process(msg, query, bot);
if (res) {
msg.channel.sendMessage(res);
}
} else {
let msgs = [];
msgs.push(msg.content + " is not a valid command.");
msgs.push(" ");
msgs.push("Available commands:");
msgs.push(" ");
msg.channel.sendMessage(msgs);
msg.channel.sendMessage(commands.help.process(msg));
}
});
bot.on('error', e => { console.error(e); });
bot.login("mytoken");
The youtubetest.js file:
var youtube_node = require('youtube-node');
var ConfigFile = require("C:/Users/username/Documents/Coding/Discord/json_config.json");
var mybot = require("C:/Users/username/Documents/Coding/Discord/mybot.js");
function myyt () {
this.youtube = new youtube_node();
this.youtube.setKey(ConfigFile.youtube_api_key);
this.vidid = "";
}
myyt.prototype.respond = function(query, msg) {
this.youtube.search(query, 1, function(error, result) {
if (error) {
msg.channel.sendMessage("There was an error finding requested video.");
} else {
vidid = 'http://www.youtube.com/watch?v=' + result.items[0].id.videoId;
myyt.vidid = vidid;
console.log("1");
}
});
console.log("2");
};
module.exports = myyt;
As the code shows, i have an object for the commands that the bot will be able to process, and I have a function to run said commands when a message is received.
Throughout the code you can see that I have put three console.logs with 1, 2 and 3 showing in which order I expect the parts of the code to run. When the code is run and a query is found the output is this:
I am ready!
string +
2
3
1
This shows that the code is running in the wrong order that I expect it to.
All help is very highly appreciated :)
*Update! Thank you all very much to understand why it isn't working. I found a solution where in the main file at vidid = youtubetest.respond(query, msg) when it does that the variable is not assigned until the function is done so it goes onto the rest of my code without the variable. To fix I simply put an if statement checking if the variable if undefined and waiting until it is defined.*
Like is mentioned before, a lot of stuff in javascript runs in async, hence the callback handlers. The reason it runs in async, is to avoid the rest of your code being "blocked" by remote calls. To avoid ending up in callback hell, most of us Javascript developers are moving more and more over to Promises. So your code could then look more like this:
myyt.prototype.respond = function(query, msg) {
return new Promise(function(resolve, reject) {
this.youtube.search(query, 1, function(error, result) {
if (error) {
reject("There was an error finding requested video."); // passed down to the ".catch" statement below
} else {
vidid = 'http://www.youtube.com/watch?v=' + result.items[0].id.videoId;
myyt.vidid = vidid;
console.log("1");
resolve(2); // Resolve marks the promises as successfully completed, and passes along to the ".then" method
}
});
}).then(function(two) {
// video is now the same as myyt.vidid as above.
console.log(two);
}).catch(function(err) {
// err contains the error object from above
msg.channel.sendMessage(err);
})
};
This would naturally require a change in anything that uses this process, but creating your own prototypes seems.. odd.
This promise returns the vidid, so you'd then set vidid = youtubetest.response(query, msg);, and whenever that function gets called, you do:
vidid.then(function(id) {
// id is now the vidid.
});
Javascript runs async by design, and trying to hack your way around that leads you to dark places fast. As far as I can tell, you're also targetting nodeJS, which means that once you start running something synchronously, you'll kill off performance for other users, as everyone has to wait for that sync call to finish.
Some suggested reading:
http://callbackhell.com/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://stackoverflow.com/a/11233849/3646975
I'd also suggest looking up ES6 syntax, as it shortens your code and makes life a hellofalot easier (native promises were only introduced in ES6, which NodeJS 4 and above supports (more or less))
In javascript, please remember that any callback function you pass to some other function is called asynchronously. I.e. the calls to callback function may not happen "in order". "In order" in this case means the order they appear on the source file.
The callback function is simply called on certain event:
When there is data to be processed
on error
in your case for example when the youtube search results are ready,
'ready' event is received or 'message' is received.
etc.
I'm using Electrons Quick Start Projekt (Commit dbef48ee7d072a38724ecfa57601e39d36e9714e) to test exceptions.
In index.html I changed the name of the required module from renderer.js to rendererXXX.js.
require('./renderer.js')
which results in an expected Exeption (it is visible in the devtools for that window):
Uncaught Error: Cannot find module './rendererXXX.js'
Now it would be nice if the main-process (see main.js) is aware that one renderer process failed. Thus I wrapped the instatiation of the window into a try-catch-block
try {
app.on('ready', createWindow)
} catch (e) {
console.log("Exception caught: " + e.message);
} finally {
// nothing yet
}
But I realized, that the Exception is not forwarded to the main-process. So what are typical ways to handle exceptions of renderer processes - is there a way to handle them from the main-process?
EDIT:
I also wrapped the line that loads the index.html into try-catch, but still I can't handle the error:
try {
// and load the index.html of the app.
mainWindow.loadURL(`file://${__dirname}/index.html`)
} catch (e) {
console.log("Exception caught in 'createWindow': " + e.message);
}
Electron windows are rendered in their own process. Because of this there is little if any communication between main process and render processes. The best you can do is catch errors in the render process and use Electrons IPC module to pass them back to your main process.
In your render process:
var ipc = require('electron').ipcRenderer;
window.onerror = function(error, url, line) {
ipc.send('errorInWindow', error);
};
In your main process:
var ipc = require('electron').ipcMain;
ipc.on('errorInWindow', function(event, data){
console.log(data)
});
Additionally your main process can watch for a limited set of events directly on the window (or on the windows webContents):
window.on('unresponsive', function() {
console.log('window crashed');
});
...
window.webContents.on('did-fail-load', function() {
console.log('window failed load');
});
I had a similar issue where I wanted to log errors to a file from the main process. Here's an addition to the answer already provided by Teak:
var ipc = require('electron').ipcRenderer;
window.onerror = function(error, url, line) {
ipc.send('errorInWindow', error);
};
would work. Just keep in mind that the onerror callback passes 5 arguments, where the last one is the actual Error object.
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror
However, since messages are serialized when sending through IPC, it's not possible to pass the Error object fully since it won't serialize correctly by default. Thus the data needs to be massaged before sending it if you need more error details (such as stack trace etc).
I used the following Is it not possible to stringify an Error using JSON.stringify? for some ideas and the end result was:
var objFromError = function(err, filter, space) {
var plainObject = {};
Object.getOwnPropertyNames(err).forEach(function(key) {
plainObject[key] = err[key];
});
return plainObject;
};
window.onerror = function (msg, url, lineNo, columnNo, error) {
ipcRenderer.send('asynchronous-windowerr', 'main', objFromError(error));
}
Then in main.js:
ipcMain.on('asynchronous-windowerr', function(event, source, err) {
var str = source + ': ';
if(err != null) {
if(err.stack != null) {
str += err.stack;
} else if(err.message != null) {
str += err.message;
}
}
loggerr.appendLogFile(errLogFile, 'err', str);
})
I have a function that tests whether a file exists or not before editing it. I use fs.stat.
fs.stat('../fill/bower.json', function (err, stats) {
if (err) {
console.log('You don\'t have a ' + clc.red('bower.json') + ' file! Type ' + clc.bgBlack.white('touch bower.json') + ' to get started.');
return;
} if (stats.isFile()) {
var json = JSON.parse(fs.readFileSync('../bower.json', 'utf8')),
string = '\n Dependencies: ' + json;
fs.writeFile('../fill/README.md,', string, 'utf8');
console.log('it\'s saved!');
}
})
However, every time I run it (bower.json doesn't exist on purpose), it returns undefined before You don't have a bower.json file!. Why does this happen and how can I stop the function printing undefined?
Edit: for reference, here's my terminal window after running the command:
Why is undefined printed, and what do I do to have that not be displayed?
You're returning nothing or undefined from your reading function.
Gist for posterity
In order to make this question as useful to as many people as possible, I will exclude my specific implementation details beyond that fact that I am using the Bluebird promise library with Node + Express below.
So, let's say that I have the following chain (where P returns a promise, and res is the Express HTTP response object):
P().then(function(){
// do nothing if all went well (for now)
// we only care if there is an error
}).catch(function(error){
res.status(500).send("An error occurred");
}).then(function(){
return P();
}).then(function(pVal1){
return [pVal1, P()];
}) // TODO: catch an error from P() here and log pVal1
.spread(function(pVal1, pVal2){
if(pVal1 === pVal2) {
console.log("Success!");
} else {
console.log("Failure");
}
});
Where I have placed the TODO comment above is where I would like to catch an error that might occur from my call to P. If I do catch an error, I would like to log pVal1 and then send a 500 error, as is done in the first catch. However, I am not sure if this is possible with how I am structuring my chain.
I believe that I need to do some "branching," but I do not think that I understand this concept well enough to stop the asynchronous nature of JavaScript from getting the best of me! As such, any help is thoroughly appreciated.
Don't forget to catch errors in the end of the chain. That's also the place to send the response.
Catching errors in the middle of a chain is for intermittent error handling; the chain continues to run, so don't send a response just yet.
Here is something to try it out:
// example middleware
function handle(req, res, next) {
log("----------------");
return async("p1", "foo").then(function (pVal1) {
return pVal1;
}).then(function (pVal1) {
var p2a = async("p2a", "bar"),
p2b = async("p2a", "bar").catch(function (error) {
log("Logging: " + error + " (pVal1 " + pVal1 + ")");
});
return [p2a, p2b];
}).spread(function (pVal1, pVal2) {
if (pVal1 === pVal2) {
res.send("Success!");
} else {
res.send("Failure");
}
}).catch(function (error) {
res.status(500).send("An error occurred");
log("Logging: " + error);
});
}
// ---------------------------------------------------------------------
// mockup response object
var res = {
status: function (code) {
log("Sending status: " + code);
return this;
},
send: function () {
log("Sending response: " + [].join.call(arguments, " "));
return this;
}
};
// mockup promise generator
function async(name, value) {
return new P(function (resolve, reject) {
if ( confirm("let " + name + " succeed?") ) {
log(name + " succeeds...");
resolve(value);
} else {
log(name + " fails...");
reject(name + " has failed");
}
});
}
function log() {
var msg = document.createElement("DIV");
msg.textContent = [].join.call(arguments, " ");
document.getElementById("log").appendChild(msg)
document.body.scrollTop = document.body.scrollHeight;
}
button {
position: fixed;
top: 5px;
}
<script src="http://cdnjs.cloudflare.com/ajax/libs/bluebird/2.9.33/bluebird.min.js"></script>
<button onclick="handle(null, res, null)">Go</button>
<div id="log"></div>
This is possible if you use the explicit Promise.all instead of returning an array into .spread.
}).then(function(pVal1){
// this becomes a `Promise.all` - the aggregation is explicit
var all = Promise.all([pVal1, P()]);
all.catch(function(e){ // "branching", we both return and `catch` the promise
console.log("Error, pVal1 is", pVal1);
});
return all; // return it
}).spread(function(pVal1, pVal2){
// ....
});
How do I test a custom module which is simply running a node-fluent-ffmpeg command with Mocha&Chai?
// segment_splicer.js
var config = require('./../config');
var utilities = require('./../utilities');
var ffmpeg = require('fluent-ffmpeg');
module.exports = {
splice: function(raw_ad_time, crop) {
if (!raw_ad_time || !crop) throw new Error("!!!!!!!!!! Missing argument");
console.log("##### LAST SEGMENT IS BEING SPLITTED.");
var segment_time = utilities.ten_seconds(raw_ad_time);
var last_segment_path = config.akamai_user_base + 'segment' + (segment_time + 1) + "_" + config.default_bitrate + "_av-p.ts?sd=10&rebase=on";
var command = ffmpeg(last_segment_path)
.on('start', function(commandLine) {
console.log('##### COMMAND: ' + commandLine);
})
.seekInput('0.000')
.outputOptions(['-c copy', '-map_metadata 0:s'])
.duration(crop)
.on('error', function(err, stdout, stderr) {
throw new Error('##### VIDEO COULD NOT BE PROCESSED: ' + err.message);
console.log('##### VIDEO COULD NOT BE PROCESSED: ' + err.message);
})
.output('public/' + 'segment' + (segment_time + 1) + "_" + config.default_bitrate + "_av-p.ts").run();
}
}
Here is what I tried:
// test/segment_splicer.js
var expect = require('chai').expect;
var segment_splicer = require('../lib/segment_splicer');
describe('Segment Splicer', function() {
it('should work', function(done) {
expect(segment_splicer.splice(1111111, 20)).to.throw(Error);
done();
});
});
I get this:
1) Segment Splicer should work:
AssertionError: expected undefined to be a function
Because I receive undefined from segment_splicer.spice method.
Thank you!
This test should be passing.
A test will only fail if you either assert or expect something in the test which is not true, or if the subject under test throws an uncaught error.
You are not asserting anything in your test, and the only error your subject will throw is if you pass less than 2 arguments, which is not the case in your test.
The ffmpeg method also seems to be asynchronous, which is not compatible with the way you have structured your test.
There are many examples available on setting up async tests, including:
How Mocha Makes Testing Asynchronous JavaScript Processes
Fun
Asynchronous Unit Tests With Mocha, Promises, And
WinJS
Testing Asynchronous
JavaScript
You've gone some way to doing this by referencing the done argument. When this is specified, Mocha will wait until it is called before considering the test finished.