Created
September 28, 2017 14:07
-
-
Save yeraydiazdiaz/a94aa3b341f1946a84f4a47ac3715555 to your computer and use it in GitHub Desktop.
A port of the recursive HN comment calculator used in "Asyncio Coroutine Patterns" using Node.js async/await
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* A port of the recursive HN comment calculator used in | |
* "Asyncio Coroutine Patterns" using Node.js async/await | |
* | |
* Assume we want to calculate the number of comments of a particular post in | |
* Hacker News by recursively aggregating the number of descendants. | |
* | |
* Original article at: | |
* https://medium.com/python-pandemonium/asyncio-coroutine-patterns-beyond-await-a6121486656f | |
* | |
* And Python code: | |
* https://github.com/yeraydiazdiaz/asyncio-coroutine-patterns/blob/master/01_recursive_coroutines/recursive_coroutines.py | |
*/ | |
const url = require('url') | |
const axios = require('axios') | |
const winston = require('winston') | |
const ArgumentParser = require('argparse').ArgumentParser | |
const instance = axios.create({ | |
baseURL: 'https://hacker-news.firebaseio.com/v0/item/', | |
timeout: 5000, | |
}) | |
/** | |
* Fetches a single post by ID. | |
* @param {number} id - The post's ID. | |
* @return {object} The `data` of the Axios response. | |
*/ | |
async function fetchPost(id) { | |
const start = new Date() | |
const response = await instance.get(`${id}.json`) | |
const end = new Date() | |
winston.debug( | |
`Fetching post with ID ${id} took ${(end - start) / 1000} seconds`) | |
return response.data | |
} | |
/** | |
* Calculates the number of comments for a post | |
* @param {number} id - The post's ID. | |
* @return {number} The number of comments for the post. | |
*/ | |
async function getNumberOfComments(id) { | |
const response = await fetchPost(id) | |
if (!response.hasOwnProperty('kids')) return 0 | |
let numberOfComments = response.kids.length | |
const results = await Promise.all(response.kids.map(getNumberOfComments)) | |
numberOfComments += results.reduce((sum, value) => sum + value) | |
winston.debug(`${id} > ${numberOfComments} comments`) | |
return numberOfComments | |
} | |
/** | |
* Gets the post ID from a URL | |
* @param {string} URL - The post's URL. | |
* @return {int} The post's ID. | |
*/ | |
function getIDFromURL(URL) { | |
const id = url.parse(URL).query.slice(3) | |
return parseInt(id) | |
} | |
/** | |
* Entry point to the script when ran directly in the console | |
* @param {object} args - Arguments from `argparse`. | |
*/ | |
function cli() { | |
const parser = new ArgumentParser({ | |
addHelp: true, | |
description: 'Recursive HN comment calculator using Node.js async/await', | |
}) | |
parser.addArgument('--id', { | |
help: 'ID of the post in HN, defaults to 8863', | |
type: 'int', | |
defaultValue: 8863, | |
}) | |
parser.addArgument('--url', { | |
help: 'URL of a post in HN, use instead of `id`', | |
type: 'string', | |
}) | |
parser.addArgument('-v', { | |
help: 'Verbose output`', | |
action: 'storeTrue', | |
}) | |
const args = parser.parseArgs() | |
if (!args.id && !args.url) { | |
throw new Error('Too few arguments, please use either `id` or `url`') | |
} | |
winston.level = args.v ? 'debug' : 'info' | |
const id = args.url ? getIDFromURL(args.url) : args.id | |
winston.info(`Calculating comments for post ${id}`) | |
const start = new Date() | |
getNumberOfComments(id) | |
.then((res) => { | |
const elapsed = (new Date().getTime() - start.getTime()) / 1000 | |
winston.info(`Number of comments: ${res}, took: ${elapsed} seconds`) | |
}) | |
.catch((error) => winston.error(error)) | |
} | |
if (require.main.id === 'module') { | |
exports.default = getNumberOfComments | |
} else { | |
cli() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment