📚 5 min read
Sequential Tasks Examples ​
This page demonstrates practical examples of executing tasks in sequence, ensuring each task completes before the next begins.
Basic Sequential Execution ​
typescript
// Basic sequential task execution
async function executeSequentially<T>(
tasks: Array<() => Promise<T>>
): Promise<T[]> {
const results: T[] = [];
for (const task of tasks) {
const result = await task();
results.push(result);
}
return results;
}
// Usage
const tasks = [
async () => {
await delay(1000);
return 'Task 1';
},
async () => {
await delay(500);
return 'Task 2';
},
async () => {
await delay(800);
return 'Task 3';
},
];
const results = await executeSequentially(tasks);
console.log('Results:', results);
Data Pipeline Processing ​
typescript
// Sequential data pipeline
class DataPipeline<T> {
private steps: Array<(data: T) => Promise<T>> = [];
addStep(step: (data: T) => Promise<T>) {
this.steps.push(step);
return this;
}
async process(initialData: T): Promise<T> {
let data = initialData;
for (const step of this.steps) {
try {
data = await step(data);
} catch (error) {
console.error('Pipeline step failed:', error);
throw error;
}
}
return data;
}
}
// Usage
interface UserData {
id: string;
name: string;
email: string;
preferences?: UserPreferences;
permissions?: UserPermissions;
}
const pipeline = new DataPipeline<UserData>()
.addStep(async (user) => {
// Load user preferences
const preferences = await fetchUserPreferences(user.id);
return { ...user, preferences };
})
.addStep(async (user) => {
// Load user permissions
const permissions = await fetchUserPermissions(user.id);
return { ...user, permissions };
})
.addStep(async (user) => {
// Validate complete user data
if (!user.preferences || !user.permissions) {
throw new Error('Incomplete user data');
}
return user;
});
const enrichedUser = await pipeline.process({
id: '123',
name: 'John Doe',
email: 'john@example.com',
});
Dependency Resolution ​
typescript
// Sequential dependency resolver
class DependencyResolver {
private dependencies = new Map<string, string[]>();
private resolved = new Set<string>();
addDependency(module: string, deps: string[]) {
this.dependencies.set(module, deps);
}
async resolve(module: string): Promise<string[]> {
const resolution: string[] = [];
await this.resolveModule(module, resolution);
return resolution;
}
private async resolveModule(
module: string,
resolution: string[]
): Promise<void> {
if (this.resolved.has(module)) return;
const deps = this.dependencies.get(module) || [];
for (const dep of deps) {
await this.resolveModule(dep, resolution);
}
resolution.push(module);
this.resolved.add(module);
}
}
// Usage
const resolver = new DependencyResolver();
resolver.addDependency('app', ['database', 'auth']);
resolver.addDependency('database', ['config']);
resolver.addDependency('auth', ['config']);
resolver.addDependency('config', []);
const loadOrder = await resolver.resolve('app');
console.log('Load order:', loadOrder);
// Output: ['config', 'database', 'auth', 'app']
Real-World Example: User Registration Flow ​
typescript
class UserRegistrationFlow {
private steps: RegistrationStep[] = [];
private rollbackSteps: Map<string, () => Promise<void>> = new Map();
constructor(private readonly db: Database) {
this.initializeSteps();
}
private initializeSteps() {
this.addStep('validateInput', {
execute: async (data: RegistrationData) => {
if (!this.isValidEmail(data.email)) {
throw new Error('Invalid email');
}
if (data.password.length < 8) {
throw new Error('Password too short');
}
return data;
},
});
this.addStep('checkExistingUser', {
execute: async (data: RegistrationData) => {
const existing = await this.db.users.findByEmail(data.email);
if (existing) {
throw new Error('User already exists');
}
return data;
},
});
this.addStep('createUser', {
execute: async (data: RegistrationData) => {
const user = await this.db.users.create({
email: data.email,
passwordHash: await this.hashPassword(data.password),
});
this.rollbackSteps.set('createUser', async () => {
await this.db.users.delete(user.id);
});
return { ...data, userId: user.id };
},
});
this.addStep('createProfile', {
execute: async (data: RegistrationData) => {
const profile = await this.db.profiles.create({
userId: data.userId!,
name: data.name,
});
this.rollbackSteps.set('createProfile', async () => {
await this.db.profiles.delete(profile.id);
});
return data;
},
});
this.addStep('sendWelcomeEmail', {
execute: async (data: RegistrationData) => {
await this.emailService.sendWelcome(data.email);
return data;
},
});
}
async register(data: RegistrationData): Promise<User> {
let currentStep = '';
try {
for (const step of this.steps) {
currentStep = step.name;
data = await step.execute(data);
}
return await this.db.users.findById(data.userId!);
} catch (error) {
console.error(`Registration failed at step ${currentStep}:`, error);
// Rollback in reverse order
const stepsToRollback = this.steps
.slice(0, this.steps.findIndex((s) => s.name === currentStep) + 1)
.reverse();
for (const step of stepsToRollback) {
const rollback = this.rollbackSteps.get(step.name);
if (rollback) {
try {
await rollback();
} catch (rollbackError) {
console.error(
`Rollback failed for step ${step.name}:`,
rollbackError
);
}
}
}
throw error;
}
}
}
// Usage
const registrationFlow = new UserRegistrationFlow(database);
try {
const user = await registrationFlow.register({
email: 'user@example.com',
password: 'securepass123',
name: 'John Doe',
});
console.log('Registration successful:', user);
} catch (error) {
console.error('Registration failed:', error);
}
Best Practices ​
Error handling with partial completion:
typescriptasync function executeWithRollback<T>( tasks: Array<{ execute: () => Promise<T>; rollback: () => Promise<void>; }> ): Promise<T[]> { const results: T[] = []; const completed: number[] = []; try { for (let i = 0; i < tasks.length; i++) { results[i] = await tasks[i].execute(); completed.push(i); } return results; } catch (error) { // Rollback completed tasks in reverse order for (const index of completed.reverse()) { try { await tasks[index].rollback(); } catch (rollbackError) { console.error('Rollback failed:', rollbackError); } } throw error; } }
Progress tracking:
typescriptasync function executeWithProgress<T>( tasks: Array<() => Promise<T>>, onProgress: (completed: number, total: number) => void ): Promise<T[]> { const results: T[] = []; const total = tasks.length; for (let i = 0; i < tasks.length; i++) { results[i] = await tasks[i](); onProgress(i + 1, total); } return results; }
Timeout handling:
typescriptasync function executeWithTimeout<T>( task: () => Promise<T>, timeout: number ): Promise<T> { return Promise.race([ task(), new Promise<never>((_, reject) => setTimeout(() => reject(new Error('Task timeout')), timeout) ), ]); } // Usage with sequential execution async function executeSequentiallyWithTimeout<T>( tasks: Array<() => Promise<T>>, timeout: number ): Promise<T[]> { return Promise.all(tasks.map((task) => executeWithTimeout(task, timeout))); }
Resource management:
typescriptclass ResourceManager { private resources: Resource[] = []; async executeWithResources<T>( task: (resources: Resource[]) => Promise<T> ): Promise<T> { try { // Acquire resources sequentially for (const resource of this.requiredResources) { this.resources.push(await resource.acquire()); } return await task(this.resources); } finally { // Release resources in reverse order for (const resource of this.resources.reverse()) { await resource.release(); } } } }