Thursday, March 15, 2012

When a JavaScript function is not a function

One would think that a function is a function is a function. Right? Wrong.

Consider this node.js code:

var f = function () {}
console.log('f instanceof Function in the main context: ' + (f instanceof Function))

require('vm').runInNewContext(
"console.log('f instanceof Function in child context: ' + (f instanceof Function))",
{ console: console, f: f })

Turns out that a function in the main V8 context is not a function in the child V8 context, at least according to JavaScript's instanceof:

f instanceof Function in the main context: true
f instanceof Function in child context: false

The reason for this can be easily explained after reading (and probably re-reading a few times) the great write-up on prototypical inheritance. The f function is created in the main context and has main context's Function object in its prototype chain. When instanceof is run in the child context against the f function created in the main context, it fails to find the child context's Function object in f's prototype chain. Knowing that, the problem can be fixed by sharing main context's Function object with the child context using the global object:

var f = function () {}
console.log('f instanceof Function in the main context: ' + (f instanceof Function))

require('vm').runInNewContext(
"console.log('f instanceof Function in child context: ' + (f instanceof Function))",
{ console: console, f: f, Function: Function })

Which leads to the expected:

f instanceof Function in the main context: true
f instanceof Function in child context: true

However, sharing objects between V8 contexts probably defeats the purpose of using separate V8 contexts in the first place, at least for some applications. After all, V8 contexts are meant to isolate in memory state. So a safe alternative that does not require sharing the Function object is using

typeof f === 'function'

check instead of the original

f instanceof Function

End of trivia.

Thursday, March 1, 2012

Running node.js code in 2 mouse clicks using haiku-http

If you are reading this, you have obviously clicked already once to get to this post. So you only have 1 click left – use it on one of the sample links below.

Then you can experiment more with haiku-http, read about haiku-http vision and design, or get the code on GitHub.

Hello, world

Here is the ‘Hello, world’ rite of passage:

http://haiku.cloudapp.net/?x-haiku-handler=https://raw.github.com/tjanczuk/haiku-http/master/samples/haikus/hello.js

res.writeHead(200)
res.end('Hello, world!\n')

War and peace

How many times do the words “war” and “peace” appear on http://reuters.com today?

War:
http://haiku.cloudapp.net/?x-haiku-handler=https://raw.github.com/tjanczuk/haiku-http/master/samples/haikus/request.js&word=war

Peace:
http://haiku.cloudapp.net/?x-haiku-handler=https://raw.github.com/tjanczuk/haiku-http/master/samples/haikus/request.js&word=peace

var query = require('url').parse(req.url, true).query
var word = query.word || 'the'
var request = require('request')
request('http://www.reuters.com', function (error, response, body) {
if (error || response.statusCode !== 200) {
res.writeHead(500)
res.end('Unexpected error getting http://reuters.com.\n')
}
else {
var count = 0, index = 0
while (0 !== (index = (body.indexOf(word, index) + 1)))
count++
res.writeHead(200)
res.end('Number of times the word "' + word + '" occurs on http://reuters.com is: ' + count + '\n')
}
})

Fetch data from MongoDB

Return documents from MongoDB that match search criteria.

All documents:
http://haiku.cloudapp.net/?x-haiku-handler=https://raw.github.com/tjanczuk/haiku-http/master/samples/haikus/mongo.js

Only documents for ‘app1.com’ host:
http://haiku.cloudapp.net/?x-haiku-handler=https://raw.github.com/tjanczuk/haiku-http/master/samples/haikus/mongo.js&host=app1.com

var query = require('url').parse(req.url, true).query
var mongoUrl = query['db'] || 'mongodb://arr:arr@staff.mongohq.com:10024/arr'
var filter = query['host'] ? { hosts: query['host'] } : {}

require('mongodb').connect(mongoUrl, function (err, db) {
if (notError(err))
db.collection('apps', function (err, apps) {
if (notError(err))
apps.find(filter).toArray(function (err, docs) {
if (notError(err)) {
res.writeHead(200)
res.end(JSON.stringify(docs))
}
})
})
})

function notError(err) {
if (err) {
res.writeHead(500)
res.end(err)
}
return !err
}

Time for more

Experiment more with haiku-http, read about haiku-http vision and design, or get the code on GitHub.

Comments welcome!

My Photo
My name is Tomasz Janczuk. I am currently working on my own venture - Mobile Chapters (http://mobilechapters.com). Formerly at Microsoft (12 years), focusing on node.js, JavaScript, Windows Azure, and .NET Framework.