5 Replies - 697 Views - Last Post: 30 January 2016 - 02:33 AM

#1 jsJim   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 2
  • Joined: 28-January 16

What's With All the Anonymous / Inline Functions in JS Literature?

Posted 28 January 2016 - 02:26 PM

Hi! I'll start off by saying that I really do like javascript, and I plan to keep using it. Hopwever, I've been reading a lot and it seems like most Javascript tutorials, documentation, and books love to use the inline anonymous function style which to me is so confusing and cyptic!

I came from a background in Java and actionscript 3. There were plenty of callback functions in these languages, but we just used named functions, like this:
var myVar;

function myFunction() {
    myVar = setTimeout(alertFunc, 3000);
}

function alertFunc() {
    alert("Hello!");
}



In javascript, I keep coming across incredibly ugly and unreadable code (in examples meant to teach people javascript!), like this:
var myVar;

function myFunction() {
    myVar = setTimeout(function(){
        alertFunc("First param", "Second param"); 
         }, 2000);
}



I don't see how the second way of writing the timeout function could possibly be cleaner or clearer than the first. And that is what I'M going for when making a 'style guide' for how my team and I should write functions- readability and clarity.

To me even one anonymous function looks bad, but thinking you are being tricky and cute by making something like this is just terribly bad for the maintainability of the code:

var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory});
p_client.open(function(err, p_client) {
    p_client.dropDatabase(function(err, done) {
        p_client.createCollection('test_custom_key', function(err, collection) {
            collection.insert({'a':1}, function(err, docs) {
                collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) {
                    cursor.toArray(function(err, items) {
                        test.assertEquals(1, items.length);
​
                        // Let's close the db​
                        p_client.close();
                    });
                });
            });
        });
    });
});



Although I'm trying to be open-minded, I don't see anything good about writing functions this way other than it saves a few keystrokes and lines (which to me is not at all important compared to code clarity). Are there any fancy scoping rules or something that might make me reconsider writing functions this way? Are there any times where I should want to write functions like this, and and do you guys feel when you come across code like this?

Is This A Good Question/Topic? 0
  • +

Replies To: What's With All the Anonymous / Inline Functions in JS Literature?

#2 Atli   User is offline

  • Enhance Your Calm
  • member icon

Reputation: 4241
  • View blog
  • Posts: 7,216
  • Joined: 08-June 10

Re: What's With All the Anonymous / Inline Functions in JS Literature?

Posted 28 January 2016 - 05:23 PM

It is clearly easy to over-use this kind of syntax, like you do in your example. However the solution isn't to avoid anonymous functions entirely, but rather apply them sanely. The same base principles apply to JS, as they do to all languages: that the code be easily readable. The developers just have to apply same common sense to their writing.

Quote

In javascript, I keep coming across incredibly ugly and unreadable code (in examples meant to teach people javascript!), like this:
var myVar;

function myFunction() {
    myVar = setTimeout(function(){
        alertFunc("First param", "Second param"); 
         }, 2000);
}


The indentation there is a little off, which makes it less readable than it aught to be, but in general using an anonymous function as a callback to simple, single-callback scenarios like that is standard in Javascript.

For a trivial example like that, it's pretty much just a matter of personal preference, but once you start writing hundreds/thousands of lines, creating a named function for all callbacks starts adding up, and ends up just making things harder to follow.

You'll end up grouping callbacks farther from the calling function than you do in your example, which causes the reader to have to jump back and forth within the code, which doesn't help. If the callback only consists of a few simple lines, you're usually better of just leaving it in plain sight.

Of course, if the callback is extensive, giving it a good name and moving it out of there is the logical thing, as it would be in any other language.


It's not a coincidence that most languages these days have emulated Javascript, and introduced anonymous functions. They can definitely be used to simplify code, as long as they aren't abused.
Was This Post Helpful? 0
  • +
  • -

#3 ArtificialSoldier   User is offline

  • D.I.C Lover
  • member icon

Reputation: 2510
  • View blog
  • Posts: 7,611
  • Joined: 15-January 14

Re: What's With All the Anonymous / Inline Functions in JS Literature?

Posted 28 January 2016 - 05:28 PM

It's not "tricky and cute", it's practical. Why bother to create a bunch of named functions in a global scope that you don't control when you're only going to use them once? What do you gain?

Is this code really any easier to follow or understand:

var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory});
p_client.open(afterOpen);

function afterOpen(err, db) {
  p_client.dropDatabase(afterDropDb);
}

function afterDropDb(err, done) {
  p_client.createCollection(afterCreateCollection);
}

function afterCreateCollection(err, collection) {
  collection.insert({'a':1}, , afterInsert);
}

function afterInsert(err, docs) {
  collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, afterFind);
}

function afterFind(err, cursor) {
  cursor.toArray(filterCursor);
}

function filterCursor(err, items) {
  test.assertEquals(1, items.length);
​
  // Let's close the db​
  p_client.close();
}


Other differences with that code are that you've now introduced several new identifiers into the scope that may cause conflicts with other libraries, and you're also relying on global variables like p_client instead of using them in a closure scope. There may also be problems passing or defining variables in one function where you're trying to use them in another function (e.g., the collection variable being used in the afterInsert function). Those problems go away when you define everything in a closure.
Was This Post Helpful? 0
  • +
  • -

#4 Atli   User is offline

  • Enhance Your Calm
  • member icon

Reputation: 4241
  • View blog
  • Posts: 7,216
  • Joined: 08-June 10

Re: What's With All the Anonymous / Inline Functions in JS Literature?

Posted 28 January 2016 - 06:08 PM

The best alternative to the excessively nested callbacks, as per that example, would probably be to define the thing as a proper object, split into logical groups.

Something along the lines of:
var IntegrationTest = function() {
    var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory});
    
    /** 
     * Private Initialization method, to reset the test environment before the test. 
     * @param {Function} callback
     */
    var initialize = function(callback) {
        if (p_client.is_closed()) {
            p_client.open(initialize);
        }
        p_client.dropDatabase(function(err, done) {
            p_client.createCollection('test_custom_key', function(err, collection) {
                return callback((boolean)err);
            });
        });
    };
    
    /**
     * Executes the test, returning the success in the callback parameter.
     * @param {Function} callback
     */
    this.execute = function(callback) {
        initialize(function(err) {
            collection.insert({'a':1}, function(err, docs) {
                collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) {
                    cursor.toArray(function(err, items) {
                        p_client.close();
                        test.assertEquals(1, items.length);
                        return callback(1 == items.length);
                    });
                });
            });
        });
        
    };
};



Then the main code would be exceedingly simple:
var test = new IntegrationTest();
test.execute(function(success) {
    console.log("Integration Test Done: " + (success ? "OK" : "FAILED");
});



This isn't far off from how you would approach this problem in a classical OOP language like Java, although there you'd most likely be defining more "private" functions to handle callbacks, whereas in JS you only have two named functions. I'd argue that the above version is simpler to read than that.
Was This Post Helpful? 0
  • +
  • -

#5 Dormilich   User is offline

  • 痛覚残留
  • member icon

Reputation: 4251
  • View blog
  • Posts: 13,479
  • Joined: 08-June 10

Re: What's With All the Anonymous / Inline Functions in JS Literature?

Posted 29 January 2016 - 12:43 AM

felgallís 2 cents
Was This Post Helpful? 0
  • +
  • -

#6 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7507
  • View blog
  • Posts: 15,558
  • Joined: 16-October 07

Re: What's With All the Anonymous / Inline Functions in JS Literature?

Posted 30 January 2016 - 02:33 AM

I've had a think on this. My first reaction is that syntax is syntax: deal. ;)

However, if you're doing Node.js server side Javascript, like the OP's example ( found here ) you have access to a few things to ease the pain.

A minor adjustment to the error first call pattern would be to use ES6 Arrow Functions. So:
var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), { 'pk': CustomPKFactory });
p_client.open((err, p_client) => {
  p_client.dropDatabase((err, done) => {
    p_client.createCollection('test_custom_key', (err, collection) => {
      collection.insert({ 'a': 1 }, (err, docs) => {
        collection.find({ '_id': new ObjectID("aaaaaaaaaaaa") }, (err, cursor) => {
          cursor.toArray((err, items) => {
            console.log(items);
            assert.equal(1, items.length);
            // Let's close the db​
            p_client.close();
          });
        });
      });
    });
  });
});



Still a nested mess, but prettier. About that mess: it has no error checking!?! That is, it should be a lot nastier, with all those little error first callbacks being checked.

To kill two bird with one stone, I'd just treat this as async and flatten it out with said module. e.g.
var async = require('async');
var assert = require('assert');

var state = { }; /* because I need to keep a few values on my way down the chain */
async.waterfall([
  (cb) => { 
    (new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), { 'pk': CustomPKFactory }))
    .open(cb); 
  },
  (client, cb) => {
    state.client = client; 
    state.client.dropDatabase(cb);
  },
  (done, cb) => { 
    state.client.createCollection('test_custom_key', cb);
  },
  (collection, cb) => {
    state.collection = collection;
    collection.insert({ 'a': 1 }, cb);
  },
  (docs, cb) => { state.collection.find({ '_id': new ObjectID("aaaaaaaaaaaa") }, cb); },
    (cursor, cb) => { cursor.toArray(cb); },
], 
  (err, items) => {
    if (err) {
      console.log(err);
    } else {
      console.log(items);
      assert.equal(1, items.length);
    }
    // Let's close the db​
    if (state.client) { state.client.close(); }
  });
}



And here we also fix a bug in the original, where the db get's closed no matter what, not just if you get to the bottom of the stack.

Realistically, the nested thing is about style. The programmer can lay things out however they feel like. However, you will have to at least tolerate how other programmers lay their stuff out.

Hope this helps.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1