TypeScript Async Await for Beginners: setTimeout, Promises and the Forgotten await
November 6, 2026
Welcome to episode 25 of TypeScript from Zero, a series for complete beginners (no JavaScript needed). Welcome back to TypeScript from zero! Today: code that has to wait. Reading a file, asking a server, waiting a second: none of this is instant, and a program must not freeze while it waits. We are in the shop project, with a new file called async.ts. Every code block below is the exact code and output from the video, ready to copy.
- All episodes are in the TypeScript from Zero playlist.
- Versions used: Node.js 26.13.0, npm 11.20.0, TypeScript 7.0.2.
Setup
We start in the terminal and open the project in VS Code:
cd shop
touch src/async.ts
code .1. Callback
The simplest tool is set timeout. We give it a function, and a time in milliseconds. The function is called later. Look at the order: first, then second, and only after one second, the message from the timer. The program did not wait. It moved on. That function is called a callback.
src/async.ts:
console.log("first");
setTimeout(() => {
console.log("one second later");
}, 1000);
console.log("second");$ node src/async.ts
first
second
one second later2. Promise
Callbacks get messy, so there is a nicer tool. A promise is an object that stands for a value that will arrive later. Our wait function returns a promise of nothing, which is void, and it is finished when the timer ends. With dot then, we say what to do next.
src/async.ts:
console.log("first");
setTimeout(() => {
console.log("one second later");
}, 1000);
console.log("second");
function wait(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
wait(500).then(() => console.log("done waiting"));$ node src/async.ts
first
second
done waiting
one second later3. async await
Even nicer: the words async and await. Await pauses the function until the promise is ready, and gives us its value. Inside an async function, the code reads from top to bottom, like normal code. And the return type is a promise of a number, not a number, because the answer comes later.
src/async.ts:
console.log("first");
setTimeout(() => {
console.log("one second later");
}, 1000);
console.log("second");
function wait(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
wait(500).then(() => console.log("done waiting"));
async function getNumber(): Promise<number> {
await wait(500);
return 42;
}
async function main() {
const n = await getNumber();
console.log("Got", n + 1);
}
main();$ node src/async.ts
first
second
done waiting
Got 43
one second later4. Forgotten await
The most common mistake: we forget the await. Now bad is not a number, but a promise. Node happily adds one to it and prints a strange text: object Promise one. tsc catches it: operator plus can not be applied to types promise of number, and one. It even asks: did you forget to use await? TS2365.
src/async.ts:
console.log("first");
setTimeout(() => {
console.log("one second later");
}, 1000);
console.log("second");
function wait(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
wait(500).then(() => console.log("done waiting"));
async function getNumber(): Promise<number> {
await wait(500);
return 42;
}
async function main() {
const n = await getNumber();
console.log("Got", n + 1);
}
main();
const bad = getNumber();
console.log(bad + 1);$ node src/async.ts
first
second
[object Promise]1
done waiting
Got 43
one second later$ npx tsc --noEmit
src/async.ts:20:13 - error TS2365: Operator '+' cannot be applied to types 'Promise<number>' and '1'.
20 console.log(bad + 1);
~~~~~~~
src/async.ts:20:13 - Did you forget to use 'await'?
20 console.log(bad + 1);
~~~~~~~
Found 1 error in src/async.ts:205. Fix
We delete those two lines, and tsc is quiet again.
src/async.ts:
console.log("first");
setTimeout(() => {
console.log("one second later");
}, 1000);
console.log("second");
function wait(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
wait(500).then(() => console.log("done waiting"));
async function getNumber(): Promise<number> {
await wait(500);
return 42;
}
async function main() {
const n = await getNumber();
console.log("Got", n + 1);
}
main();$ npx tsc --noEmit6. Top level
Because our project is a module, we can also use await at the top level of the file, with no main function. We wait, and then print the number.
src/async.ts:
console.log("first");
setTimeout(() => {
console.log("one second later");
}, 1000);
console.log("second");
function wait(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
wait(500).then(() => console.log("done waiting"));
async function getNumber(): Promise<number> {
await wait(500);
return 42;
}
async function main() {
const n = await getNumber();
console.log("Got", n + 1);
}
main();
const top = await getNumber();
console.log("Top level:", top);$ node src/async.ts
first
second
done waiting
Got 43
Top level: 42
one second laterRecap
Today: set timeout and callbacks, promises with then, async and await, the type promise of T, and the forgotten await. This was the last episode of the type system part. Next episode: we start a real project, a task tracker for the command line.