Programming Models: Sequential, Concurrent, and Parallel

Sequential Programming

  • Sequential programming is the traditional approach where tasks are executed one after another.
  • Each task must complete before the next one begins.
  • It's simple to understand but can be inefficient when dealing with multiple operations.
function task1() {
  // Task 1 execution
}

function task2() {
  // Task 2 execution
}

task1();
task2(); // Task 2 starts only after Task 1 finishes

Characteristics of Sequential Programming

  • Single task at a time.
  • Blocking: One operation must complete before the next one starts.
  • Example: Reading a file and processing its content one after another.

Concurrent Programming

  • Concurrent programming refers to executing multiple tasks in overlapping time periods.
  • Tasks don't necessarily execute at the same time, but their progress is interleaved.
function task1() {
  // Task 1 execution
}

function task2() {
  // Task 2 execution
}

setTimeout(task1, 1000);  // Task 1 is delayed
task2();                 // Task 2 can run before Task 1 completes

Characteristics of Concurrent Programming

  • Interleaving tasks: Multiple tasks make progress within the same time frame.
  • Non-blocking: Tasks may yield control during their execution.
  • Example: A web server handling multiple user requests.

Parallel Programming

  • Parallel programming is when multiple tasks run at exactly the same time on multiple processors or cores.
  • It is a subset of concurrent programming where tasks are truly simultaneous.
// Parallel execution with multiple threads
parallelize(task1, task2);

Characteristics of Parallel Programming

  • Simultaneous execution: Tasks are executed at the same time.
  • Requires multiple processors/cores.
  • Example: Image processing where different parts of an image are processed simultaneously.

Comparing the Three Models

  • Sequential: One task at a time, linear execution.
  • Concurrent: Multiple tasks progress within the same time period.
  • Parallel: Multiple tasks execute at the same time on separate processors.

Example: Cooking Analogy

  • Sequential: Cook one dish at a time, finishing one before starting another.
  • Concurrent: Start cooking one dish while another is simmering.
  • Parallel: Two chefs cooking two dishes at the same time on separate stoves.

Conclusion

  • Sequential, concurrent, and parallel programming address different needs.
  • Choosing the right model depends on the problem and hardware capabilities.

Introduction to Promises in JavaScript

What is a Promise?

A Promise in JavaScript is an object that represents the eventual completion (or failure) of an asynchronous operation.

const promise = new Promise((resolve, reject) => {
  // asynchronous operation
});

States of a Promise

Promises have three states:

  1. Pending: Initial state, neither fulfilled nor rejected.
  2. Fulfilled: The operation completed successfully.
  3. Rejected: The operation failed.
let promise = new Promise((resolve, reject) => {
  // initially pending
});

Example of Promise Resolution

The resolve() function signals the success of the asynchronous operation.

const promise = new Promise((resolve, reject) => {
  setTimeout(() => resolve("Success!"), 1000);
});

promise.then(result => console.log(result)); // "Success!" after 1 second

Example of Promise Rejection

The reject() function is used to signal failure in the operation.

const promise = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error("Failure!")), 1000);
});

promise.catch(error => console.error(error)); // Error: Failure!

Promise Methods: then, catch, finally

  • then(): Used to handle the success case.
  • catch(): Used to handle errors or rejection.
  • finally(): Runs regardless of the outcome.
promise
  .then(result => console.log(result))
  .catch(error => console.error(error))
  .finally(() => console.log("Operation complete"));

Chaining Promises

Promises can be chained to handle sequences of asynchronous operations.

fetchData()
  .then(data => processData(data))
  .then(result => displayResult(result))
  .catch(error => handleError(error));

Async/Await: Modern Approach

async/await simplifies working with promises by allowing you to write asynchronous code that looks synchronous.

async function fetchData() {
  try {
    let data = await fetch('https://api.example.com/data');
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

What is Async/Await?

  • Async/Await is a syntactic sugar built on top of Promises, introduced in ECMAScript 2017 (ES8).
  • It allows you to write asynchronous code that reads and behaves like synchronous code.
  • Makes code more readable and easier to maintain, especially for complex chains of Promises.
async function fetchData() {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  console.log(data);
}

How async Works

  • When a function is declared as async, it automatically returns a Promise.
  • The async keyword allows the use of the await keyword within that function.
async function myAsyncFunction() {
  return "Hello, Async!";
}

myAsyncFunction().then(result => console.log(result)); // "Hello, Async!"
  • Without async, the above function would just return a regular value, not a Promise.

How await Works

  • The await keyword is used inside async functions to pause the execution of the function until the Promise is resolved.
  • It makes asynchronous code behave as if it were synchronous, without blocking the main thread.
async function fetchData() {
  let response = await fetch('https://api.example.com/data'); 
  let data = await response.json(); 
  console.log(data); 
}

Key Characteristics of await

  • Pauses execution: await pauses the function execution until the promise is settled (either resolved or rejected).
  • Non-blocking: Even though await pauses the async function, it does not block the execution of other code outside the function.
async function demo() {
  console.log("Start");
  let result = await someAsyncTask();
  console.log(result);
}
console.log("Code after async call");  // This will run immediately

Handling Errors with Async/Await

  • Instead of using .catch() to handle errors in Promises, async/await allows you to handle errors with try-catch blocks.
async function fetchData() {
  try {
    let response = await fetch('https://api.invalid-url.com');
    let data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("An error occurred:", error);
  }
}

Parallelism with Async/Await

  • Multiple awaits in a function will run sequentially by default. However, you can execute Promises in parallel by calling them first and then awaiting their resolution.
async function loadData() {
  let promise1 = fetch('https://api.example.com/data1');
  let promise2 = fetch('https://api.example.com/data2');
  
  let [data1, data2] = await Promise.all([promise1, promise2]);
  console.log(data1, data2);
}

Using Promise.all() with Async/Await

  • Promise.all() allows you to run multiple asynchronous tasks in parallel and wait for all of them to complete.
  • This is useful when you have independent asynchronous operations that can be executed simultaneously.
async function getData() {
  let [result1, result2] = await Promise.all([
    fetch('https://api.example.com/data1'),
    fetch('https://api.example.com/data2')
  ]);

  console.log(result1, result2);
}

Benefits of Async/Await

  • Simplicity: Makes code more readable and easier to maintain compared to Promise chains.
  • Error handling: Uses try-catch, making error management more straightforward.
  • Non-blocking: Code execution continues normally without being blocked by async operations.

Async/Await vs Promises

  • Promises: Use .then() and .catch() for handling asynchronous operations.
  • Async/Await: Allows asynchronous code to be written in a synchronous style using await and try-catch for error handling.
Feature Promises Async/Await
Syntax More verbose Cleaner, more readable
Error Handling .catch() try-catch
Execution Style Chain-based Synchronous-looking
Performance Can be parallelized Can use Promise.all() for parallelism

Conclusion

  • Async/Await simplifies asynchronous programming by making it more readable.
  • It builds on top of Promises and is great for writing cleaner, more maintainable code.
  • Always consider combining it with Promise.all() for parallelism when needed.

Conclusion

  • Promises help manage asynchronous code in a cleaner and more readable way.
  • Methods like then(), catch(), and finally() offer flexibility.
  • Async/await provides a simpler syntax for working with promises.

Programming Models: Sequential, Concurrent, and Parallel

By Néstor Aldana

Programming Models: Sequential, Concurrent, and Parallel

  • 131