You have your environment set up and a nomada-tasks folder with its index.html, its CSS and a js/app.js that prints a message. It is time to turn that into your first real program. In this lesson you will learn to display information with console.log, to include JavaScript in a web page in the various ways available (and to understand what defer and async mean), to run a file with Node.js, to document your code with comments and —very importantly— to read an error message without panicking. By the end you will have written a program that prints the summary of a Taller Nómada task.
Contents
console.log: your window into the program- Writing code straight into the console
- Including JavaScript in a page: inline versus external file
- Where to put the
<script>:deferandasync - Running JavaScript with Node.js
- Your first program: a task summary
- Comments in your code
- How to read an error message
- Common Mistakes and Tips
- Exercises
- Conclusion
console.log: your window into the program
console.log: your window into the programA program does things internally that you cannot see. console.log is the tool that lets you take a peek at what is going on: you pass it a value and it writes it to the browser console (or to the terminal, if you are using Node.js).
Let's take the line apart piece by piece, because every symbol counts:
| Part | What it is |
|---|---|
console |
An object the environment makes available to you |
.log |
A function belonging to that object |
( ... ) |
The parentheses indicate that you are calling the function |
'Hello, Taller Nómada' |
The argument: the value you pass in |
; |
End of the statement |
If you write console.log without parentheses, you are not calling anything: you are only mentioning the function. It is like pointing at a light switch instead of flipping it.
1.1 Printing several values at once
You can pass several arguments separated by commas. console.log prints them all, separated by a space:
This is far more convenient than concatenating strings, and it has an extra advantage: each value is shown with its type. In the browser console, the text '12' appears in a different color from the number 12. That visual clue will save you from more than one mistake.
1.2 Other console methods
console has more useful functions, and it is worth knowing them from the start:
| Method | What it is for | How it looks in the console |
|---|---|---|
console.log() |
General information | Plain text |
console.info() |
Highlighted information | Similar to log |
console.warn() |
Warnings | Yellow background, with an icon |
console.error() |
Errors | Red background, with an icon |
console.table() |
Tabular data | An actual table |
Using them properly keeps the console readable when there are lots of messages. A yellow warning among fifty gray lines stands out; one more console.log does not.
- Writing code straight into the console
The console does not just display: it also executes. Open the DevTools (F12), go to Console, type something and press Enter:
It answers 24. It is a JavaScript calculator always within reach, ideal for trying out one-off ideas.
Two practical details:
- To write several lines without running them, press
Shift + Enterinstead ofEnter. - The up/down arrows bring back previous commands, just like in a terminal.
The console is perfect for experimenting, but whatever you type there is lost when you reload the page. Real programs live in files.
- Including JavaScript in a page: inline versus external file
There are two ways to put JavaScript into an HTML page.
3.1 Inline script
The code is written inside the tag itself:
It works, but it is only acceptable for very quick tests.
3.2 External script
The code lives in its own .js file and the HTML links to it with the src attribute:
Let's compare them:
| Aspect | Inline | External (src) |
|---|---|---|
| Reuse across several pages | No | Yes |
| Browser cache | Not cached | Cached: later loads are faster |
| Separation of concerns | Mixes structure and behavior | Everything in its place |
| Tooling (linters, tests) | Cannot analyze it properly | Works without issues |
| Recommended | Only for tests | Always |
Golden rule: always use an external file. That is what we will do in Nómada Tasks.
3.3 Two warnings about <script>
First: if you use src, the content inside the tag is ignored entirely. This prints nothing:
<!-- Wrong! The console.log never runs -->
<script src="js/app.js">
console.log('This will never run');
</script>Second: the tag must be closed. <script src="js/app.js" /> is not valid HTML; you have to write </script>.
- Where to put the
<script>: defer and async
<script>: defer and asyncThe browser reads the HTML from top to bottom. When it hits a <script> with no extra attributes, it stops building the page, downloads the file, runs it and only then continues. That has a critical consequence.
4.1 The problem with a script in the <head>
<head>
<!-- Problem! At this point the <h1> does not exist yet -->
<script src="js/app.js"></script>
</head>
<body>
<h1>Nómada Tasks</h1>
</body>If app.js tries to access the <h1>, it will not find it: by the time the script runs, that part of the HTML has not been read yet. This is the number-one cause of the mysterious "I'm getting null" that beginners run into.
4.2 The three solutions
Option A — at the end of the <body> (what the Nómada Tasks skeleton uses):
By the time the browser reaches the script, all the HTML above it already exists. Simple, and it always works.
Option B — defer in the <head> (the recommended approach today):
defer means: download the file in parallel while you keep building the page, and run it once the HTML is complete. You gain download speed without losing the guarantee.
Option C — async (for specific cases):
async downloads in parallel and runs as soon as the download finishes, interrupting the construction of the page. It does not guarantee that the HTML is ready, nor does it respect the order between several scripts. It is only suitable for completely independent code, such as an analytics tag.
4.3 Side-by-side comparison
| Attribute | Does it block the HTML load? | When does it run? | Does it respect the order between scripts? | Recommended use |
|---|---|---|---|---|
(none) in the <head> |
Yes | Immediately after downloading | Yes | Avoid |
(none) at the end of the <body> |
Yes, but it no longer matters | When the tag is reached | Yes | Correct and simple |
defer |
No | Once the HTML is complete | Yes | The best general option |
async |
No | As soon as it is downloaded | No | Independent scripts |
flowchart TD
A["The browser starts<br/>reading the HTML"] --> B{"Does it find<br/>a script?"}
B -->|"no attributes"| C["Halts the HTML<br/>Downloads and runs it"]
B -->|"defer"| D["Downloads in parallel<br/>Runs at the end of the HTML"]
B -->|"async"| E["Downloads in parallel<br/>Runs when the download finishes"]
C --> F["The HTML continues"]
D --> F
E --> F
This is an introductory overview. The relationship between scripts, page loading and performance is studied in depth in Module 9, and accessing the elements of the page in Module 6.
- Running JavaScript with Node.js
When you want to try out code without setting up a page, Node.js is the fastest route. Create a test.js file in any folder:
And run it from the terminal, standing in the folder where it lives:
Output:
Keep an important difference between the two environments in mind:
| Browser | Node.js | |
|---|---|---|
| How it runs | By loading the page | node file.js |
Where you see console.log |
The Console tab | The terminal |
| Access to the web page | Yes | No |
| Access to files on disk | No | Yes |
| Good for | The real application | Trying out standalone logic |
The language is the same. A calculation or a comparison behaves identically in both places. What changes is what surrounds it.
- Your first program: a task summary
Now for the real thing. Open js/app.js in nomada-tasks and replace its contents with this:
// js/app.js
// Nómada Tasks — First program.
// Prints the summary of a Taller Nómada task to the console.
// 1. We store the task data in variables with descriptive names
const title = 'Redesign the multipurpose room';
const assignee = 'Iván';
const priority = 'high';
const estimatedHours = 12;
const dueDate = '2026-09-30';
// 2. We print a header to visually separate the output
console.log('=== Nómada Tasks · Task summary ===');
// 3. We print each piece of data with its label
console.log('Title: ', title);
console.log('Assignee:', assignee);
console.log('Priority:', priority);
console.log('Hours: ', estimatedHours);
console.log('Due date:', dueDate);
// 4. A warning if the priority is high
console.warn('Warning: this task has priority', priority);Save the file and look at the browser console. You should see:
=== Nómada Tasks · Task summary === Title: Redesign the multipurpose room Assignee: Iván Priority: high Hours: 12 Due date: 2026-09-30 ⚠ Warning: this task has priority high
Here is what is happening:
const name = value;creates a named container and stores a value inside it.constmeans that container will not be reassigned. The details come in the next lesson.- The names are descriptive.
estimatedHourstells you what it holds;horxdoes not. Writing clear names is not cosmetics: it is the difference between being able to read your own code a month from now and not. - Text goes in quotes, numbers do not.
'high'is text;12is a number. If you wrote'12'with quotes, JavaScript would treat it as text and you would not be able to add it up properly. This nuance is explained in Variables and Data Types. - The program runs from top to bottom, line by line, in the order it is written.
Try running the same file with Node.js: node js/app.js from the project folder. You will see exactly the same output in the terminal. It is the same language.
- Comments in your code
A comment is text that JavaScript ignores completely. It exists to explain the reasons behind things to whoever reads the code later (very likely you).
7.1 Single-line comment: //
// This whole line is a comment
const assignee = 'Lucía'; // It can also go at the end of a line of code7.2 Multi-line comment: /* */
7.3 Commenting out to disable code
A very practical use: temporarily disabling a line without deleting it while you hunt for a bug.
In VS Code, Ctrl + / comments or uncomments the line (or the selection) instantly.
7.4 What to comment and what not to
| Comment | Assessment |
|---|---|
// Adds 1 to i above i = i + 1 |
Useless: it repeats what the code already says |
// Iván only works half days on Fridays |
Useful: it explains a business rule that is invisible in the code |
// TODO: validate that the date is not in the past |
Useful: it records outstanding work |
// fixed on 3/12, do not touch |
Bad: it explains nothing; that is Git's job |
The practical rule: the code says the "what", the comment says the "why". If you need a comment to explain what a line does, what you probably need is a better name.
- How to read an error message
Errors are not punishments: they are information. Learning to read them is one of the skills that will speed you up the most.
Cause one on purpose in app.js:
Something like this will appear in the console:
Let's take the message apart:
| Part | Meaning |
|---|---|
Uncaught |
The error was not caught, so it stopped execution |
ReferenceError |
The type of error: a name that does not exist was used |
taskAssignee is not defined |
The description: which name is failing |
at app.js:3:13 |
The location: file app.js, line 3, column 13 |
That last part is gold. Go straight to that line. In the browser console, the app.js:3:13 link is clickable and takes you to the Sources tab with the cursor already in place.
The error types you will see in your first few weeks:
| Type | What it means | Typical cause |
|---|---|---|
SyntaxError |
The code does not follow the rules of the language | A missing quote, parenthesis or brace |
ReferenceError |
A name that does not exist is used | A typo in a variable name |
TypeError |
Something impossible is done with a value | Calling something as a function when it is not one |
RangeError |
A value is outside the allowed range | A negative number where none is accepted |
There is an important difference between the first one and the rest: a SyntaxError prevents the entire file from running, because the engine has to understand all the code before it can start. The others happen during execution and stop the program at that point, leaving everything before it already executed.
8.1 Day-one errors
| Symptom | Likely cause | How to confirm it |
|---|---|---|
| Absolutely nothing happens | The .js file was not loaded |
Network tab: does it show up with a 404? |
| "I can't see my messages" | The console is not open or a filter is active | Open Console and check the log-level selector |
| Accented characters look odd | <meta charset="UTF-8"> is missing |
Check the <head> |
| The message appears and disappears | The page is reloading (for example, when a form is submitted) | Tick Preserve log in the console |
Uncaught SyntaxError |
A quote, parenthesis or brace was left unclosed | Go to the line the error points to, and check the previous one too |
A methodological tip: when something fails, look at the console before doing anything else. And fix the first error in the list, not the last one: cascading errors are usually a consequence of the first.
Common Mistakes and Tips
Common mistakes
- Writing
console.logwithout parentheses. It prints nothing; it merely mentions the function. - Wrong capitalization.
Console.log()givesReferenceError: Console is not defined. JavaScript is case-sensitive. - Mixing quote characters.
'Nómada"is aSyntaxError. Start and end with the same kind of quote. - Putting code inside a
<script src="...">. It is ignored entirely. - Putting the script in the
<head>withoutdefer. When the script runs, the HTML does not exist yet. - Confusing the console with the editor. What you type in the console is lost on reload; the program lives in the files.
- Getting scared by a red error. The message tells you the type, the cause and the exact line. It is the best help you are going to get.
Tips
- Print often while you are learning. A well-placed
console.loganswers "does the program even get here?" and "what is this worth right now?". - Label your messages:
console.log('hours:', hours)instead ofconsole.log(hours). With ten messages on screen you will be grateful. - Use
deferin new projects. It is the correct default behavior. - Save and reload frequently. Small changes and frequent checks pinpoint the bug straight away.
- Keep a
test.jshandy to experiment withnode test.jswithout touching the project.
Exercises
Exercise 1: A task card in the console
Create a file js/exercise-01.js that prints the card for this Taller Nómada task:
- Title:
Set up the screen-printing workshop - Assignee:
Marta - Priority:
medium - Estimated hours:
6 - Due date:
2026-10-15
Requirements:
- Use one variable per piece of data, with a descriptive name.
- Print a header line with the name of the project.
- Print each piece of data with a label.
- Finish with a
console.infothat says:Card generated successfully. - Include a block comment at the top of the file with the name of the exercise.
- Run it both ways: linked from the HTML and with
node js/exercise-01.js.
Exercise 2: Error diagnosis
For each snippet, state what type of error occurs, what message you will see roughly, and how you would fix it:
Exercise 3: Decide how the script loads
For each situation, say which option you would use (<script> at the end of the <body>, defer or async) and why:
- The Nómada Tasks
js/app.jsneeds to read and modify the task list in the HTML. - A visitor-statistics script that does not touch the page and can run whenever.
- Two files,
js/data.jsandjs/app.js, where the second depends on the first having run already.
Solutions
Exercise 1
/*
Nómada Tasks — Module 1, Exercise 1
A Taller Nómada task card printed to the console.
*/
// Task data
const title = 'Set up the screen-printing workshop';
const assignee = 'Marta';
const priority = 'medium';
const estimatedHours = 6;
const dueDate = '2026-10-15';
// Header
console.log('=== Nómada Tasks · Task card ===');
// Labeled data
console.log('Title: ', title);
console.log('Assignee:', assignee);
console.log('Priority:', priority);
console.log('Hours: ', estimatedHours);
console.log('Due date:', dueDate);
// Confirmation
console.info('Card generated successfully.');Linked from the HTML you see it in the Console tab; with node js/exercise-01.js, in the terminal. The output is identical, because the language is the same in both environments.
Exercise 2
| Case | Error type | Approximate message | Fix |
|---|---|---|---|
| A | SyntaxError |
Invalid or unexpected token |
Close the quote: console.log('Task for Lucía'); |
| B | ReferenceError |
Console is not defined |
Write console in lowercase |
| C | ReferenceError |
assinee is not defined |
Fix the typo: assignee |
| D | SyntaxError |
Unexpected string |
Add the parentheses: console.log('Task for Iván'); |
Notice the pattern: cases A and D are syntax errors (the engine does not understand the code and runs nothing at all from the file); B and C are reference errors (the code is valid, but when it runs it asks for something that does not exist).
Exercise 3
defer, or a script at the end of the<body>. It needs the HTML to be complete before it runs.deferis preferable: it downloads in parallel and runs once the page is ready.async. It does not depend on the page or on other scripts, and the sooner it runs the better. This is exactly its use case.deferon both, in that order (data.jsbeforeapp.js), or both at the end of the<body>in that order. What has to be avoided isasync: it does not guarantee execution order, soapp.jscould run beforedata.jsand fail intermittently in a way that is impossible to reproduce.
Conclusion
You have written and run your first JavaScript program. You know how to use console.log and its variants (warn, error, info, table) to see what is going on inside the program; you can tell an inline script from an external one and you know why you should always use external files; you understand why the position of the <script> matters and exactly what defer and async do; you can run any file with node file.js; you document with comments that explain the why, not the what; and —perhaps most valuable of all— you know how to read an error message: its type, its description and the exact line where it happens.
And in js/app.js you already have a real program that prints the summary of a Taller Nómada task, with its title, assignee, priority, hours and due date.
So far you have been copying syntax without fully knowing its rules. In the next lesson, JavaScript Syntax and Basic Concepts, you will put names to what you have been doing: what exactly a statement is, when the semicolon is needed, how {} blocks work, what names you can give your variables and what strict mode is. It is the lesson that turns "I copy it and it works" into "I understand why it works".
JavaScript Course: From Beginner to Advanced
Module 1: Introduction to JavaScript
- What Is JavaScript?
- Setting Up Your Development Environment
- Your First JavaScript Program
- JavaScript Syntax and Basic Concepts
- Variables and Data Types
- Basic Operators
- Type Conversion and Comparisons
- The Course Project: Nómada Tasks
Module 2: Control Structures
- Conditional Statements
- Loops: for, while, do-while
- Switch Statements
- Flow Control: break, continue and Nested Loops
- Error Handling with try-catch
Module 3: Functions
- Defining and Calling Functions
- Function Expressions and Arrow Functions
- Parameters and Return Values
- Scope and Closures
- Hoisting and the Execution Context
- Higher-Order Functions
- Recursion
Module 4: Objects and Arrays
- Introduction to Objects
- Object Methods and the
thisKeyword - Arrays: Basics and Methods
- Iterating over Arrays
- Searching, Sorting and Aggregating Data: find, sort and reduce
- Array Destructuring
- Object Destructuring, Spread and Rest
- JSON and Copying Objects
Module 5: Advanced Objects and Functions
- Prototypes and Inheritance
- Classes and Object-Oriented Programming
- Encapsulation: Getters, Setters and Private Fields
- Modules: Import and Export
- Asynchronous JavaScript: Callbacks
- Promises and Async/Await
- The Event Loop and the Microtask Queue
- Iterators and Generators
Module 6: The Document Object Model (DOM)
- Introduction to the DOM
- Selecting and Manipulating DOM Elements
- Handling Events
- Propagation, Delegation and Custom Events
- Creating and Removing DOM Elements
- Rendering Lists and HTML Templates
- Handling and Validating Forms
Module 7: Browser APIs and Advanced Topics
- Local and Session Storage
- The Fetch API and AJAX
- Robust Requests: Errors, Timeouts and AbortController
- WebSockets
- Service Workers and Progressive Web Apps (PWAs)
- Essential Browser APIs
- Introduction to WebAssembly
Module 8: Testing and Debugging
- Debugging JavaScript
- Code Quality: ESLint, Prettier and Conventions
- Unit Testing with Jest
- Test Doubles: Mocks, Stubs and Spies
- Integration Testing
- End-to-End Testing with Cypress
Module 9: Performance and Optimization
- Measure Before You Optimize: DevTools and Web Vitals
- Optimizing JavaScript Performance
- Memory Management
- Efficient DOM Manipulation
- Lazy Loading and Code Splitting
Module 10: JavaScript Frameworks and Libraries
- Why Frameworks Exist
- Introduction to React
- State Management with Redux
- Vue.js Basics
- Angular Basics
- Choosing the Right Framework
