Home > Enterprise >  Difference in outcome when Node code is in function vs. out of function
Difference in outcome when Node code is in function vs. out of function

Time:09-26

Okay, so I have what is effectively two identical (to my eyes at least!) pieces of Node JS code. The only difference is that in once case it is wrapped up in a function and called via that function and in the other case, the code sits outside any function. When I run the two pieces, of what should be identical code, I get vastly different results. Why is this?

Here is the code when not inside of a function:

chunks = []
req = https.get('https://www.example.com',
                res => {
                    res.setEncoding('utf8')
                    res.on('data', chunk => {
                        chunks.push(chunk)
                    })
                    res.on('end', function(){
                        chunks.join()
                    })
                }).end()
console.log(chunks[0])

Here is me using what, as far as I can tell, should effectively be the exact same code but via a function format:

function readURI(uri){
    const chunks = []
    const req = https.get(uri, res => {
        res.setEncoding('utf8')
        res.on('data', chunk => {
            chunks.push(chunk)
        })
        res.on('end', function(){
            chunks.join()
        })
    })
    req.end()
    return chunks[0]
}
console.log(readURI('https://www.example.com'))

I run both of these in the Node REPL. In the first instance, chunks[0] is a string with the content of the website as I expect. In the second, I get no data in the return of the function. Why would this happen?

CodePudding user response:

There are two things at play here.

  • When a line is evaluated, the completion value of the line is displayed when using the Node REPL - even if the line doesn't include a console.log. As a result, the first code's req = https.get('https://www.example.com', logs the request object.

  • Look at what gets logged carefully - it really does not contain the content of the website anywhere - the request is asynchronous, it has not finished by the time the final line is executed.

Welcome to Node.js v14.17.6.
Type ".help" for more information.
> const https = require('https');
undefined
> chunks = []
[]
> req = https.get('https://www.example.com',
...                 res => {
.....                     res.setEncoding('utf8')
.....                     res.on('data', chunk => {
.......                         chunks.push(chunk)
.......                     })
.....                     res.on('end', function(){
.......                         chunks.join()
.......                     })
.....                 }).end()
ClientRequest {
  _events: [Object: null prototype] {
    response: [Function: bound onceWrapper] { listener: [Function (anonymous)] }
  },
  _eventsCount: 1,
  _maxListeners: undefined,
  outputData: [
    {
      data: 'GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: close\r\n\r\n',
      encoding: 'latin1',
      callback: [Function: bound onFinish]
    }
  ],
  outputSize: 60,
  writable: true,
  destroyed: false,
  _last: true,
  chunkedEncoding: false,
  shouldKeepAlive: false,
  _defaultKeepAlive: true,
  useChunkedEncodingByDefault: false,
  sendDate: false,
  _removedConnection: false,
  _removedContLen: false,
  _removedTE: false,
  _contentLength: 0,
  _hasBody: true,
  _trailer: '',
  finished: true,
  _headerSent: true,
  socket: null,
  _header: 'GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: close\r\n\r\n',
  _keepAliveTimeout: 0,
  _onPendingData: [Function: noopPendingOutput],
  agent: Agent {
    _events: [Object: null prototype] {
      free: [Function (anonymous)],
      newListener: [Function: maybeEnableKeylog]
    },
    _eventsCount: 2,
    _maxListeners: undefined,
    defaultPort: 443,
>
    options: { path: null },
    requests: {},
    sockets: { 'www.example.com:443:::::::::::::::::::::': [Array] },
    freeSockets: {},
    keepAliveMsecs: 1000,
    keepAlive: false,
    maxSockets: Infinity,
    maxFreeSockets: 256,
    scheduling: 'lifo',
    maxTotalSockets: Infinity,
    totalSocketCount: 1,
    maxCachedSessions: 100,
    _sessionCache: { map: {}, list: [] },
    [Symbol(kCapture)]: false
  },
  socketPath: undefined,
  method: 'GET',
  maxHeaderSize: undefined,
  insecureHTTPParser: undefined,
  path: '/',
  _ended: false,
  res: null,
  aborted: false,
  timeoutCb: null,
  upgradeOrConnect: false,
  parser: null,
  maxHeadersCount: null,
  reusedSocket: false,
  host: 'www.example.com',
  protocol: 'https:',
  [Symbol(kCapture)]: false,
  [Symbol(kNeedDrain)]: false,
  [Symbol(corked)]: 0,
  [Symbol(kOutHeaders)]: [Object: null prototype] { host: [ 'Host', 'www.example.com' ] }
}
> console.log(chunks[0])
undefined
undefined
>

The website content is not in there - in either version of your code, as expected. It's just that your first code has the request as a completion value of a line, but the second code does not - and in the function version, chunks is an empty array when readURI finishes, so undefined is returned.

Unless you're sure you know exactly how the REPL works, to avoid confusing yourself, I'd recommend running scripts as their own files - eg node example.js - for anything that isn't exceedingly trivial.

CodePudding user response:

This was a simple mistake in interacting with the REPL. In entering the code into the REPL, I did so without a new line on the last line of code and so the request object has been made and completed by the time I hit enter on the final console.log line. In the case of the function based snippet, this doesn't matter as the request is made in the same line and doesn't have time to complete by the time the log is made.

  • Related