March 20, 2015

jshint warning “Don’t make functions within a loop”

node.jsEver get JSHint warning “Don’t make functions within a loop” with your node.js code? I do and my OCD strives to stamp out this warning in my code.

A typical situation for me is I want to augment a model before returning from GET REST call.   Here is a stripped down example:

'use strict';
/*jshint node: true */
var data_users = [
{
  "id": "aaaa",
  "name": "moe"
},
{
  "id": "bbbb",
  "name": "larry"
},
{
  "id": "cccc",
  "name": "curly"
}];


function fakeRedis(key, cb)
{
  var data_ttl = {
    "aaaa": 600
  };
  var result = data_ttl[key];
  if (!result)
  {
    result = -2;
  }
  cb(null, result);
}

function preResolveHook(users)
{
  for (var i in users)
  {
   if (users.id)
   {
    fakeRedis(users[i].id, function(err, ttl)
    {
       if (users[i])
       {
          users[i].ttl = ttl;
          if (i === users.length - 1)
          {
            console.log(users);
            // next();
          }
        }
      });
    }
  }
}

preResolveHook(data_users);

Run jshint on the above code I get

bad.js: line 48, col 14, Don't make functions within a loop.

Using ‘Q’ for promises I reworked the code to fire off all the FakeRedis calls and stuff the returned promises in an array. Then I use Q.all([promises]) to wait for all the FakeRedis calls to complete and then augment the model with the result.

<pre>'use strict';
/*jshint node: true */
var Q = require('q');
var data_users = [
{
  "id": "aaaa",
  "name": "moe"
},
{
  "id": "bbbb",
  "name": "larry"
},
{
  "id": "cccc",
  "name": "curly"
}];


function fakeRedis(key, cb)
{
  var data_ttl = {
    "aaaa": 600
  };
  var result = data_ttl[key];
  if (!result)
  {
    result = -2;
  }
  cb(null, result);
}

function promiseTTL(id)
{
  var defer = Q.defer();
  fakeRedis(id, function(err, ttl)
  {
    if (err)
    {
      console.log(err);
      defer.resolve(-3);
    }
    else
    {
      defer.resolve(ttl);
    }
  });
  return defer.promise;
}

function promisePreResolveHook(users)
{
  var pArray = [];
  for (var i in users)
  {
    if (users[i].id)
    {
      pArray.push(promiseTTL(users[i].id));
    }
  }
  Q.all(pArray).then(function(array)
  {
    for (var i in array)
    {
      if (array[i])
      {
        users[i].ttl = array[i];
      }
    }
    // next();
  });
}

promisePreResolveHook(data_users);

If you have a better way of handling this situation I would love to hear from you!