Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Tuesday, November 19, 2024

JavaScript Web Worker Thread Example

Web Workers enable running scripts in the background independently of the main UI thread. This allows complex computations to be performed without blocking the user interface. Some facts are as follows:
  • DOM manipulation is not allowed inside worker file and cannot use window object properties or methods.
  • Communication between main thread and worker happens using postMessage() and response to message via onmessage event handler.
  • Dedicated worker uses single script file
  • Shared worker uses multiple script files.
Here’s an example of implementing a Web Worker in a simple HTML project.

Step 1: Create the HTML Document
Create an HTML file with two buttons (Calculate and Welcome) and two <div> elements for displaying output. The HTML structure is as follows:
<!DOCTYPE html>
<html>
  <head>
    <title>JavaScript Workers</title>
  </head>
  <body>
    <div>
      <h1>All About JS Workers</h1>
    </div>
    <button id="btn1">Calculate</button>
    <button id="btn2">Welcome</button>
    <br /><br />
    <div id="value1"></div>
    <div id="value2"></div>

    <script src="complex.js"></script>
    <script>
      let counter = 0;
      const btnWelcome = document.getElementById("btn2");

      // Handle "Welcome" button click
      btnWelcome.addEventListener("click", () => {
        counter++;
        document.getElementById("value2").innerHTML = `Welcome... ${counter}`;
      });
    </script>
  </body>
</html>
Step 2: Create the External JavaScript File (complex.js)
The complex.js file contains the logic to create a Web Worker and communicate with it. This script handles the Calculate button click and executes complex computations using a background worker (worker.js).
const btn = document.getElementById("btn1");

btn.addEventListener("click", () => {
  // Check if the browser supports Web Workers
  if (window.Worker) {
    const wrkObj = new Worker("worker.js");

    // Send a message to the worker
    wrkObj.postMessage("Start worker...");

    // Receive data from the worker
    wrkObj.onmessage = function (e) {
      console.log("Received data:", e.data);
      document.getElementById("value1").innerHTML = `<h1>${e.data}</h1>`;
    };
  }
});

Explanation

  • Creating the Worker: The Worker object is instantiated with the reference to worker.js.
  • Communication:
    • The postMessage() method sends data to the worker.
    • The worker responds using the onmessage event handler.
  • Browser Support: The script checks for window.Worker to ensure Web Worker support.

Step 3: Create the Worker Script (worker.js)

The worker.js file performs a computationally intensive task in the background. This script communicates with the main thread via postMessage() and onmessage.
// DOM manipulation is not allowed in the worker
// Communication happens via postMessage and onmessage

onmessage = function (e) {
  let sum = 0;

  // Perform a complex operation (e.g., calculating a large sum)
  for (let i = 0; i < 9999999999; i++) {
    sum += i;
  }

  console.log(sum);

  // Send the result back to the main thread
  postMessage(sum);
};

Key Points

  1. Background Execution: The worker runs independently of the main UI thread.
  2. No DOM Manipulation: Workers cannot access the DOM or window object.
  3. Single Script: A dedicated worker is associated with a single script file.
  4. Communication: Messages are exchanged between the main thread and worker using postMessage() and onmessage.

Summary

  1. HTML Document: Contains UI elements and inline script for basic functionality.
  2. complex.js: Handles Web Worker creation and communication.
  3. worker.js: Performs the background task and sends the result to the main thread.
This example demonstrates how Web Workers can offload intensive tasks, keeping the UI responsive. Use Web Workers whenever you need to perform CPU-intensive tasks in your JavaScript applications.



JavaScript Promise in Hindi

जावा स्क्रिप्ट के अंतर्गत प्रॉमिस एक ऑब्जेक्ट होता है जिसका निर्माण new ऑपरेटर के माध्यम से करने पर उसके भीतर निहित executor फंक्सन रन होता है। इस फंक्शन के रन होने के पश्चात भविष्य में प्रॉमिस दो में से एक स्थिति के अनुसार एक रिजल्ट देता है यदि परिस्थिति अनुकूल है तो प्रॉमिस सक्सेस से संबंधित रिजल्ट देता है और यदि स्थिति विपरीत है तो प्रॉमिस रिजेक्ट से संबंधित फल देता है।

let executorFunction = function(resolve, reject){
    // logic here
}
const myPromise = new Promise(executorFunction);

प्रॉमिस के भीतर परिस्थिति के अनुकूल या प्रतिकूल होने से संबंधित कुछ कंडीशन होते हैं। यह कंडीशन एक या एक से अधिक स्टेटमेंट के द्वारा कोड में परिलक्षित होता है। resolve reject में से एक ही कालबैक रन हो सकता हैं इसकी शर्त executorFunction के भीतर होता है 

const myPromise = new Promise(function(resolve, reject){
    // logic here
});

यहां समझने वाली बात यह है कि executor फंक्सन  को दो पैरामीटर पास किया जाता है यह दोनों पैरामीटर वस्तुतः फंक्शंस होते हैं। फंग्शनल प्रोग्रामिंग के सिद्धांत के अनुसार किसी फंक्शन को भी दूसरे फंक्शन के पैरामीटर के रूप में पास किया जा सकता है। ऐसे function parameter को callback कहते हैं।

कहने का अभिप्राय यह हुआ कि हम एग्जीक्यूटर फंक्शन के भीतर दो कॉलबैक फंक्शन को पैरामीटर के रूप में पास करते हैं।

ध्यान दीजिए कि callback function को भी पैरामीटर pass किया जा सकता है और यह किसी भी प्रकार का data हो सकता है और यह एक function भी हो सकता है।

जब एक प्रॉमिस ऑब्जेक्ट को new ऑपरेटर की सहायता से क्रिएट किया जाता है तो वह ऑब्जेक्ट मेमोरी में स्टोर हो जाता है और प्रॉमिस का जो भी कॉलबैक फंक्शन है वह नियत अवधि के बाद रन किया जा सकता है और इसके लिए हमें प्रॉमिस ऑब्जेक्ट को कंज्यूम करना होता है। प्रॉमिस ऑब्जेक्ट को कंज्यूम करने के लिए जो तरीका है वह हम आगे बताते हैं। प्रॉमिस ऑब्जेक्ट को कंज्यूम करने के लिए हम then मेथड का उपयोग करते हैं।

याद रखें की fetch एपीआईं हमें एक प्रॉमिस रिटर्न करता है।

जावास्क्रिप्ट में प्रॉमिस (Promises): Revised

जावास्क्रिप्ट में प्रॉमिस एक ऐसा ऑब्जेक्ट होता है, जो असिंक्रोनस ऑपरेशन (Asynchronous Operation) का परिणाम दर्शाने के लिए उपयोग किया जाता है। इसका निर्माण new ऑपरेटर के माध्यम से किया जाता है। जब प्रॉमिस का निर्माण होता है, तो इसके भीतर एक एग्जीक्यूटर फंक्शन (Executor Function) तुरंत रन होता है। यह फंक्शन भविष्य में होने वाली घटनाओं के आधार पर प्रॉमिस को दो में से किसी एक स्थिति में ले जाता है:
  1. Fulfilled (Resolved): जब ऑपरेशन सफल होता है।
  2. Rejected: जब ऑपरेशन असफल होता है।

प्रॉमिस और उसकी कार्यप्रणाली

एग्जीक्यूटर फंक्शन:
जब new Promise लिखा जाता है, तो इसके भीतर एक एग्जीक्यूटर फंक्शन दिया जाता है। यह फंक्शन दो पैरामीटर स्वीकार करता है:
  1. Resolve: इसे कॉल करने पर प्रॉमिस को Fulfilled स्टेट में ले जाया जाता है।
  2. Reject: इसे कॉल करने पर प्रॉमिस को Rejected स्टेट में ले जाया जाता है।
एग्जीक्यूटर फंक्शन में ये दोनों पैरामीटर कॉलबैक फंक्शंस (Callback Functions) होते हैं। जावास्क्रिप्ट की फंक्शनल प्रोग्रामिंग की क्षमता के कारण, फंक्शन को पैरामीटर के रूप में पास करना संभव है।

उदाहरण 1: प्रॉमिस का निर्माण और उपयोग

const myPromise = new Promise((resolve, reject) => {
    let success = true; // यहां पर किसी कंडीशन को चेक किया जा सकता है
    if (success) {
        resolve("ऑपरेशन सफल रहा!");
    } else {
        reject("ऑपरेशन असफल रहा!");
    }
});

// प्रॉमिस को कंज्यूम करना
myPromise
    .then((message) => {
        console.log(`Success: ${message}`);
    })
    .catch((error) => {
        console.log(`Error: ${error}`);
    });

आउटपुट (जब success = true):

Success: ऑपरेशन सफल रहा!
आउटपुट (जब success = false):

Error: ऑपरेशन असफल रहा!
इस उदाहरण में, प्रॉमिस ऑब्जेक्ट को कंज्यूम करने के लिए then और catch का उपयोग किया गया है।
  1. then: यह उस स्थिति को हैंडल करता है जब प्रॉमिस Fulfilled हो।
  2. catch: यह उस स्थिति को हैंडल करता है जब प्रॉमिस Rejected हो।
एग्जीक्यूटर फंक्शन के पैरामीटर
जैसा कि हमने देखा, एग्जीक्यूटर फंक्शन को दो पैरामीटर (resolve और reject) पास किए जाते हैं। ये दोनों पैरामीटर खुद फंक्शंस होते हैं।
  1. resolve(data): इस फंक्शन को कॉल करके हम प्रॉमिस को एक सक्सेस स्टेट में ले जाते हैं और साथ में डेटा पास कर सकते हैं।
  2. reject(error): इस फंक्शन को कॉल करके हम प्रॉमिस को असफल स्टेट में ले जाते हैं और साथ में एरर पास कर सकते हैं।

उदाहरण 2: प्रॉमिस और setTimeout का उपयोग

const delayedPromise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("डेटा 2 सेकंड बाद प्राप्त हुआ!");
    }, 2000);
});

delayedPromise
    .then((message) => {
        console.log(message);
    })
    .catch((error) => {
        console.log(error);
    });
आउटपुट (2 सेकंड बाद):

डेटा 2 सेकंड बाद प्राप्त हुआ!

इस उदाहरण में, setTimeout का उपयोग करके यह दिखाया गया है कि प्रॉमिस का उपयोग असिंक्रोनस ऑपरेशन को नियंत्रित करने के लिए किया जा सकता है।

प्रॉमिस का व्यवहार

  • जब एक प्रॉमिस ऑब्जेक्ट new ऑपरेटर के साथ बनाया जाता है, तो वह तुरंत एग्जीक्यूटर फंक्शन को रन करता है।
  • प्रॉमिस का रिजल्ट फुलफिल्ड या रिजेक्टेड स्थिति में रहता है।
  • प्रॉमिस के स्टेट को नियंत्रित करने के लिए then, catch, और finally का उपयोग किया जाता है।

fetch API और प्रॉमिस

जावास्क्रिप्ट में fetch API एक प्रॉमिस रिटर्न करती है। इसे HTTP कॉल्स के लिए उपयोग किया जाता है।

उदाहरण: fetch और प्रॉमिस

fetch('https://jsonplaceholder.typicode.com/posts/1')
    .then((response) => {
        if (!response.ok) {
            throw new Error("नेटवर्क में समस्या है!");
        }
        return response.json();
    })
    .then((data) => {
        console.log("डेटा प्राप्त हुआ:", data);
    })
    .catch((error) => {
        console.log("एरर:", error);
    });
आउटपुट (जब डेटा सफलतापूर्वक प्राप्त हो):

डेटा प्राप्त हुआ: { userId: 1, id: 1, title: "...", body: "..." }
आउटपुट (जब नेटवर्क समस्या हो):

एरर: नेटवर्क में समस्या है!

निष्कर्ष

  1. प्रॉमिस जावास्क्रिप्ट में असिंक्रोनस ऑपरेशन को संभालने का एक शक्तिशाली टूल है। यह कॉलबैक पद्धति को सरल बनाता है और अधिक पठनीय (Readable) कोड लिखने में मदद करता है।
  2. प्रॉमिस का उपयोग डेटा फेचिंग, टाइमर बेस्ड ऑपरेशन, और अन्य असिंक्रोनस प्रक्रियाओं को नियंत्रित करने के लिए किया जाता है।
  3. इसका उचित उपयोग कोड को प्रभावी और त्रुटि-मुक्त बनाने में सहायक है।

Promise Part2


मैं जोर देना चाहता हूँ कि प्रॉमिस के दो चरण है: प्रॉमिस का निर्माण और प्रॉमिस का उपयोग
साथ ही यह भी की प्रॉमिस के executor function के कालबैक मेथड्स कैसे किस किस तरह के data return कर सकते हैं। इस को विस्तार से समझते हैं 

जावास्क्रिप्ट में प्रॉमिस: निर्माण और उपयोग

जावास्क्रिप्ट में प्रॉमिस एक ऐसी सुविधा है, जो असिंक्रोनस ऑपरेशन्स (Asynchronous Operations) को संभालने के लिए उपयोग होती है। इसका उद्देश्य कोड को सरल और प्रभावी बनाना है, जिससे लंबे और उलझे हुए कॉलबैक के उपयोग से बचा जा सके।

प्रॉमिस को मुख्यतः दो चरणों में समझा जा सकता है:
  1. प्रॉमिस का निर्माण (Producing)
  2. प्रॉमिस का उपयोग (Consuming)

1. प्रॉमिस का निर्माण (Create Promise)

प्रॉमिस का निर्माण new ऑपरेटर और एक एग्जीक्यूटर फंक्शन के माध्यम से किया जाता है। यह चरण उस प्रक्रिया का प्रतिनिधित्व करता है, जहां प्रॉमिस के भीतर डेटा तैयार किया जा रहा होता है।

एग्जीक्यूटर फंक्शन और उसके पैरामीटर
एग्जीक्यूटर फंक्शन दो कॉलबैक फंक्शन्स को पैरामीटर के रूप में स्वीकार करता है:

Resolve callback function: जब ऑपरेशन सफल हो, तब डेटा को निर्मित प्रॉमिस ऑब्जेक्ट में भेजने के लिए इसका उपयोग किया जाता है।
Reject callback function: जब ऑपरेशन असफल हो, तब एरर या समस्या को निर्मित प्रॉमिस में भेजने के लिए इसका उपयोग किया जाता है।

उदाहरण: प्रॉमिस का निर्माण

const myPromise = new Promise((resolve, reject) => {
    let isSuccess = true; // ऑपरेशन की स्थिति चेक करें
    if (isSuccess) {
        resolve("ऑपरेशन सफल रहा!"); // जब ऑपरेशन सफल हो
    } else {
        reject("ऑपरेशन असफल रहा!"); // जब ऑपरेशन असफल हो
    }
});
इस उदाहरण में, myPromise नामक प्रॉमिस ऑब्जेक्ट बनाया गया है।

यदि स्थिति अनुकूल है (isSuccess = true), तो resolve फंक्शन को कॉल किया जाएगा।
यदि स्थिति प्रतिकूल है, तो reject फंक्शन को कॉल किया जाएगा।

2. प्रॉमिस का उपयोग (Consume Promise)

एक बार प्रॉमिस तैयार हो जाने के बाद, इसे कंज्यूम किया जा सकता है। कंज्यूम करने का मतलब है कि प्रॉमिस के परिणाम (Fulfilled या Rejected) के आधार पर प्रतिक्रिया देना।

कंज्यूम करने के लिए मेथड्स
  1. then: इसे तब उपयोग किया जाता है, जब प्रॉमिस Fulfilled हो।
  2. catch: इसे तब उपयोग किया जाता है, जब प्रॉमिस Rejected हो।
  3. finally: इसे हमेशा (चाहे प्रॉमिस Fulfilled हो या Rejected) रन किया जाता है।
उदाहरण: प्रॉमिस का उपयोग

myPromise
    .then((message) => {
        console.log(`Success: ${message}`); // Fulfilled स्थिति का हैंडलिंग
    })
    .catch((error) => {
        console.log(`Error: ${error}`); // Rejected स्थिति का हैंडलिंग
    });

एग्जीक्यूटर फंक्शन से डेटा कैसे लौटाया जाता है?
एग्जीक्यूटर फंक्शन के resolve और reject कॉलबैक्स में किसी भी प्रकार का डेटा पास किया जा सकता है:

सिंपल डेटा (जैसे: string, number)
  • resolve("यह एक साधारण संदेश है");
  • reject("यह एक त्रुटि संदेश है");

ऑब्जेक्ट या डेटा स्ट्रक्चर
  • resolve({ id: 1, name: "John Doe" });
  • reject({ errorCode: 404, message: "डेटा नहीं मिला" });
फंक्शन
  • resolve(() => console.log("यह एक फंक्शन है"));
असिंक्रोनस डेटा (जैसे: HTTP फेचिंग)

fetch('https://jsonplaceholder.typicode.com/posts/1')
    .then((response) => response.json())
    .then((data) => resolve(data))
    .catch((error) => reject(error));

उदाहरण: प्रॉमिस में डेटा लौटाना

const dataPromise = new Promise((resolve, reject) => {
    const data = { userId: 1, name: "Rahul" };
    resolve(data); // डेटा ऑब्जेक्ट को Resolve करें
});

dataPromise
    .then((result) => {
        console.log("प्राप्त डेटा:", result);
    })
    .catch((error) => {
        console.log("त्रुटि:", error);
    });

आउटपुट:
प्राप्त डेटा: { userId: 1, name: "Rahul" }

Fetch API और प्रॉमिस का उपयोग

fetch API जावास्क्रिप्ट में असिंक्रोनस डेटा फेचिंग के लिए उपयोग होती है और यह हमेशा एक प्रॉमिस रिटर्न करती है।

उदाहरण: Fetch API के साथ प्रॉमिस का उपयोग

fetch('https://jsonplaceholder.typicode.com/posts/1')
    .then((response) => {
        if (!response.ok) {
            throw new Error("नेटवर्क में समस्या!");
        }
        return response.json(); // डेटा को JSON में बदलें
    })
    .then((data) => {
        console.log("डेटा:", data);
    })
    .catch((error) => {
        console.log("त्रुटि:", error);
    });

आउटपुट (जब डेटा सफलतापूर्वक प्राप्त हो):

डेटा: { userId: 1, id: 1, title: "...", body: "..." }

प्रॉमिस का लाभ

  1. कॉलबैक हैल (Callback Hell) से बचाव।
  2. कोड अधिक पढ़ने योग्य और व्यवस्थित बनता है।
  3. कई असिंक्रोनस ऑपरेशन्स को आसानी से हैंडल किया जा सकता है।

निष्कर्ष

प्रॉमिस जावास्क्रिप्ट में असिंक्रोनस कोड को बेहतर तरीके से हैंडल करने का एक प्रभावी तरीका है। इसे दो चरणों में समझना सरल बनाता है:
  • प्रॉमिस का निर्माण (Producing), जहां डेटा तैयार होता है।
  • प्रॉमिस का उपयोग (Consuming), जहां परिणाम का उपयोग किया जाता है।

Promise Part3

अब मैं यह भी बताना चाहता हूं कि जब new ऑपरेटर के द्वारा प्रॉमिस का निर्माण किया जाता है तो वह हिप में कब तक रहता है और कब किस शर्त पर उसके भीतर के कॉल बैक एग्जीक्यूट होते हैं और कब प्रॉमिस को deallocate किया जाता है

जावास्क्रिप्ट में प्रॉमिस: हिप में जीवनकाल और निष्पादन शर्तें

जावास्क्रिप्ट में प्रॉमिस असिंक्रोनस कोड को सरल और प्रबंधनीय बनाने का एक साधन है। जब new ऑपरेटर का उपयोग करके प्रॉमिस का निर्माण किया जाता है, तो यह हिप (Heap) मेमोरी में तब तक रहता है जब तक कि उसे डीलोकेट (Deallocate) नहीं किया जाता। प्रॉमिस का जीवनकाल, निष्पादन, और डीलोकेशन विशिष्ट शर्तों पर आधारित होता है। इस निबंध में हम इन्हीं पहलुओं पर चर्चा करेंगे।

1. प्रॉमिस का निर्माण और हिप में प्रवेश

जब प्रॉमिस का निर्माण new Promise() के माध्यम से किया जाता है:
  1. जावास्क्रिप्ट प्रॉमिस ऑब्जेक्ट को हिप मेमोरी में संग्रहित करता है।
  2. इसके साथ ही, एग्जीक्यूटर फंक्शन (Executor Function) को तुरंत निष्पादित किया जाता है।
  3. एग्जीक्यूटर फंक्शन के भीतर दिए गए resolve और reject कॉलबैक्स केवल तभी निष्पादित होते हैं जब एग्जीक्यूटर फंक्शन अपने कार्य को पूरा कर लेता है।
उदाहरण: प्रॉमिस का निर्माण और हिप में जीवनकाल की शुरुआत

const myPromise = new Promise((resolve, reject) => {
    console.log("प्रॉमिस का निर्माण हो रहा है...");
    setTimeout(() => {
        resolve("डेटा तैयार है");
    }, 3000);
});
console.log("प्रॉमिस निर्माण के बाद कोड चलता है");

आउटपुट:
प्रॉमिस का निर्माण हो रहा है...
प्रॉमिस निर्माण के बाद कोड चलता है
(3 सेकंड बाद) डेटा तैयार है
प्रॉमिस का ऑब्जेक्ट हिप में तब तक रहता है, जब तक resolve या reject कॉल नहीं किया जाता।

2. प्रॉमिस के कॉलबैक के निष्पादन की शर्तें

  1. एग्जीक्यूटर फंक्शन सिंक रूप से (Synchronously) तुरंत रन होता है।
  2. हालांकि, एग्जीक्यूटर फंक्शन के भीतर resolve और reject असिंक्रोनस ऑपरेशन की स्थिति के आधार पर निष्पादित होते हैं।
  3. इवेंट लूप और प्रॉमिस: प्रॉमिस का निष्पादन इवेंट लूप (Event Loop) से नियंत्रित होता है।
  4. जब resolve या reject कॉल किया जाता है, तो प्रॉमिस का परिणाम (Result) माइक्रोटास्क क्यू (Microtask Queue) में भेजा जाता है।
  5. माइक्रोटास्क क्यू के टास्क तब निष्पादित होते हैं, जब मुख्य कोड (Call Stack) खाली हो जाता है।
उदाहरण: प्रॉमिस के कॉलबैक का समय

console.log("कार्य प्रारंभ");
const myPromise = new Promise((resolve, reject) => {
    resolve("प्रॉमिस पूरा हुआ");
});
myPromise.then((result) => {
    console.log(result);
});
console.log("कार्य समाप्त");

आउटपुट:
कार्य प्रारंभ
कार्य समाप्त
प्रॉमिस पूरा हुआ

भले ही resolve तुरंत कॉल किया गया हो, इसका परिणाम इवेंट लूप के माध्यम से माइक्रोटास्क क्यू में डालकर बाद में निष्पादित किया जाता है।

हिप में प्रॉमिस कब तक रहता है?

1. तब तक, जब तक प्रॉमिस निष्कर्ष पर न पहुंचे (Pending State):
जब तक प्रॉमिस Pending स्थिति में है, यह हिप में बना रहता है।
Pending स्थिति तब होती है जब resolve या reject को कॉल नहीं किया गया है।
जैसे ही resolve या reject कॉल किया जाता है, प्रॉमिस का निष्कर्ष तय हो जाता है।

2. परिणाम के बाद: तब तक, जब तक सभी उपभोक्ता (Consumers) निष्पादित न हो जाएं:
प्रॉमिस का रिजल्ट हिप में तब तक बना रहता है, जब तक सभी then या catch ब्लॉक्स निष्पादित न हो जाएं।

3. निष्पादन के बाद: मेमोरी से हटाना (Deallocate):
जब प्रॉमिस का परिणाम (Fulfilled या Rejected) सभी उपभोक्ताओं तक पहुंचा दिया जाता है और ऑब्जेक्ट का कोई रेफरेंस नहीं बचता, तो प्रॉमिस हिप मेमोरी से हटा दिया जाता है।
यह काम जावास्क्रिप्ट का Garbage Collector करता है।

4. प्रॉमिस का डीलोकेशन (Deallocation) शर्तें:
  1. प्रॉमिस का परिणाम तय हो चुका हो।
  2. ऑब्जेक्ट का कोई और रेफरेंस न बचा हो।
उदाहरण: डीलोकेशन के लिए शर्तें

let myPromise = new Promise((resolve, reject) => {
    resolve("डेटा तैयार है");
});

myPromise.then((data) => {
    console.log(data);
});

// जब myPromise को null कर दिया जाता है, तो यह डीलोकेट हो सकता है
myPromise = null;
myPromise को null करने पर यह हिप से हटा दिया जाएगा, क्योंकि अब इसका कोई रेफरेंस नहीं है।

प्रॉमिस के जीवनचक्र का सारांश
  1. निर्माण: new ऑपरेटर के माध्यम से प्रॉमिस हिप में प्रवेश करता है।
  2. निष्पादन: एग्जीक्यूटर फंक्शन तुरंत निष्पादित होता है, लेकिन resolve और reject असिंक्रोनस रूप से चलते हैं।
  3. परिणाम: resolve या reject के परिणाम के बाद, प्रॉमिस का परिणाम हिप में तब तक बना रहता है, जब तक सभी उपभोक्ताओं द्वारा उपयोग न हो जाए।
  4. डीलोकेशन: सभी उपभोक्ताओं के निष्पादित होने और ऑब्जेक्ट का कोई रेफरेंस न बचने पर, प्रॉमिस को गार्बेज कलेक्टर हटा देता है।
उदाहरण: प्रॉमिस का पूरा जीवनचक्र

console.log("कार्य प्रारंभ");

let myPromise = new Promise((resolve, reject) => {
    console.log("प्रॉमिस निर्माण हुआ");
    setTimeout(() => {
        resolve("डेटा तैयार");
    }, 2000);
});

myPromise.then((result) => {
    console.log(result);
    myPromise = null; // डीलोकेशन के लिए तैयार
});

console.log("कार्य समाप्त");

आउटपुट:
कार्य प्रारंभ
प्रॉमिस निर्माण हुआ
कार्य समाप्त
(2 सेकंड बाद) डेटा तैयार

निष्कर्ष

जावास्क्रिप्ट में प्रॉमिस का जीवनचक्र स्पष्ट रूप से परिभाषित है:
  • निर्माण (Producing) से प्रारंभ होकर परिणाम (Consuming) तक।
  • हिप में प्रॉमिस तब तक बना रहता है, जब तक इसका उपयोग समाप्त नहीं हो जाता।
  • जब कोई रेफरेंस शेष नहीं रहता, तो गार्बेज कलेक्टर इसे मेमोरी से हटा देता है।
  • यह प्रक्रिया प्रॉमिस के उपयोग को कुशल और संसाधन-संरक्षित बनाती है। विद्यार्थियों को इसे समझने के लिए सरल कोड और प्रयोगों से शुरुआत करनी चाहिए।

JavaScript Closure in Hindi

जब कोई जावास्क्रिप्ट सीखना शुरू करता है, तो उसके लिए क्लोजर (Closure) का कॉन्सेप्ट थोड़ा कठिन लग सकता है। इसका सबसे बड़ा कारण यह है कि अधिकांश प्रोग्रामर उन भाषाओं से आते हैं, जिनमें फंक्शनल प्रोग्रामिंग की अवधारणा कम या बिलकुल नहीं होती। उदाहरण के लिए, यदि कोई C, C#, या C++ से आता है, तो उसने आमतौर पर ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग (OOP) या प्रोसिजरल ओरिएंटेड प्रोग्रामिंग (POP) के आधार पर प्रोग्रामिंग सीखी होती है।

ऐसी प्रोग्रामिंग भाषाओं में फंक्शन को एक ऑब्जेक्ट की तरह उपयोग नहीं किया जाता। जावास्क्रिप्ट सिखाने वाले के लिए यह स्पष्ट करना जरूरी है कि जावास्क्रिप्ट में फंक्शन भी एक प्रकार का ऑब्जेक्ट होता है। यह फंक्शनल प्रोग्रामिंग की विशेषता के कारण संभव है, जिसे समझना हर जावास्क्रिप्ट प्रोग्रामर के लिए आवश्यक है।

क्लोजर की अवधारणा

जावास्क्रिप्ट में, आप किसी फंक्शन के भीतर दूसरे फंक्शन को पैरामीटर के रूप में पास कर सकते हैं और यहां तक कि एक फंक्शन के भीतर दूसरे फंक्शन को रिटर्न भी कर सकते हैं। इसका अर्थ यह है कि एक फंक्शन, किसी दूसरे फंक्शन को रिटर्न कर सकता है।

जब कोई फंक्शन किसी दूसरे फंक्शन को रिटर्न करता है, तो इनर फंक्शन (inner function) वह होता है जिसे आउटर फंक्शन (outer function) रिटर्न करता है। जब आउटर फंक्शन को कॉल किया जाता है, तो वह इनर फंक्शन का ऑब्जेक्ट रिटर्न करता है।

लेकिन यहां ध्यान देने वाली बात यह है कि रिटर्न किया गया इनर फंक्शन, हिप मेमोरी में स्टोर रहता है। जावास्क्रिप्ट का गैरेज कलेक्टर (Garbage Collector) इस फंक्शन को तब तक डीलोकेट (deallocate) नहीं करता, जब तक इसका उपयोग प्रोग्राम में हो रहा हो।

क्लोजर का व्यवहार

क्लोजर को समझने के लिए यह जानना आवश्यक है कि कोई भी फंक्शन अपने स्कोप में मौजूद डेटा को उपयोग कर सकता है। जावास्क्रिप्ट में, कोई भी इनर फंक्शन अपने आउटर फंक्शन के डेटा को एक्सेस कर सकता है। इसे हम ऐसे कह सकते हैं कि "एनक्लोजिंग फंक्शन" और "एनक्लोज्ड फंक्शन" के बीच एक संबंध होता है, जहां इनर फंक्शन, आउटर फंक्शन के डेटा को उपयोग करता है।

दूसरे शब्दों में, आउटर फंक्शन के डेटा को इनर फंक्शन द्वारा इस्तेमाल किया जा सकता है। क्लोजर के मामले में, जब आउटर फंक्शन इनर फंक्शन को रिटर्न करता है, तो इनर फंक्शन जिन वेरिएबल्स को पॉइंट करता है, वे सभी डेटा क्लोजर में स्टोर रहते हैं।

उदाहरण से क्लोजर को समझते हैं 

उदाहरण 1: आउटर और इनर फंक्शन

function outerFunction(outerVariable) {
    return function innerFunction(innerVariable) {
        console.log(`Outer Variable: ${outerVariable}`);
        console.log(`Inner Variable: ${innerVariable}`);
    };
}

// आउटर फंक्शन को कॉल करें और एक क्लोजर प्राप्त करें
const newFunction = outerFunction('Hello');
// इनर फंक्शन को कॉल करें
newFunction('World');

आउटपुट:
Outer Variable: Hello  
Inner Variable: World  

यहां, innerFunction में outerVariable का उपयोग किया गया है, जो outerFunction के स्कोप में था। लेकिन outerFunction को कॉल करने के बाद भी outerVariable को गैरेज कलेक्टर द्वारा डीलोकेट नहीं किया गया, क्योंकि इनर फंक्शन उसे उपयोग कर रहा था। यही क्लोजर की ताकत है।

उदाहरण 2: डेटा एन्कैप्सुलेशन (Data Encapsulation)

function counter() {
    let count = 0; // यह वेरिएबल क्लोजर में रहेगा
    return function() {
        count++;
        return count;
    };
}

const increment = counter();
console.log(increment()); // 1
console.log(increment()); // 2
console.log(increment()); // 3

यह उदाहरण दिखाता है कि कैसे count वेरिएबल को बाहर से एक्सेस किए बिना, increment फंक्शन के जरिए नियंत्रित किया जा सकता है। यह डेटा एन्कैप्सुलेशन का एक उत्कृष्ट उदाहरण है।

क्लोजर के उपयोग

  1. डेटा एन्कैप्सुलेशन: क्लोजर का उपयोग प्राइवेट डेटा को संरक्षित करने के लिए किया जा सकता है।
  2. मेमोरी मैनेजमेंट: उपयोग में न आने वाले वेरिएबल्स को हटाने से मेमोरी की बचत होती है।
  3. कस्टम इटररेटर्स और इवेंट हैंडलर्स: जटिल लॉजिक को संभालने के लिए क्लोजर का उपयोग किया जाता है।
  4. मेमोइज़ेशन (Memoization): किसी फंक्शन के परिणामों को स्टोर करके बार-बार की गणना से बचा जा सकता है।

निष्कर्ष

क्लोजर जावास्क्रिप्ट की एक शक्तिशाली अवधारणा है, जो इसे अन्य भाषाओं से अलग बनाती है। इसका उपयोग जटिल प्रोग्रामिंग समस्याओं को हल करने में किया जा सकता है। हालांकि शुरुआत में इसे समझना मुश्किल लग सकता है, लेकिन एक बार समझने के बाद यह आपकी प्रोग्रामिंग क्षमताओं को बहुत अधिक बढ़ा देता है।

Sunday, November 17, 2024

JavaScript Extract metadata of an audio or video file

To extract metadata from audio or video files in JavaScript, you can use several libraries and methods, depending on the type of metadata you need (e.g., duration, bitrate, codec, etc.). Below are some of the common ways to extract metadata in both the browser and Node.js environments.

1. Extracting Metadata in the Browser

In the browser, you can use the HTMLMediaElement interface, which is part of the &lt;audio&gt; and &lt;video&gt; HTML tags, to extract basic metadata like duration, width, height, and more.
Example: Extracting metadata from an audio or video file in the browser
<input type="file" id="fileInput" />
<script>
  document.getElementById("fileInput").addEventListener("change", function(event) {
    const file = event.target.files[0];
    if (file) {
      const url = URL.createObjectURL(file);

      const mediaElement = document.createElement(file.type.startsWith('audio') ? 'audio' : 'video');
      mediaElement.src = url;

      mediaElement.addEventListener("loadedmetadata", function() {
        // For video files, extract width and height
        const duration = mediaElement.duration;  // Duration in seconds
        const width = mediaElement.videoWidth;
        const height = mediaElement.videoHeight;

        console.log("Duration:", duration);
        console.log("Width:", width);
        console.log("Height:", height);

        if (file.type.startsWith('audio')) {
          // If it's an audio file, extract additional properties like duration
          console.log("Audio Duration:", duration);
        }

        // Clean up the object URL
        URL.revokeObjectURL(url);
      });
    }
  });
</script>
Explanation:
  1. loadedmetadata: This event is fired once the metadata (like duration, dimensions for video, and duration for audio) is loaded.
  2. mediaElement.duration: The total length of the media (audio or video) in seconds.
  3. mediaElement.videoWidth and mediaElement.videoHeight: These properties are for video files to get the resolution.
  4. file.type.startsWith('audio'): Used to determine if the file is audio or video based on its MIME type.

2. Using ffmpeg.js for Advanced Metadata Extraction

If you need more detailed metadata, like codec information, bitrate, or more complex details, you can use ffmpeg.js, which is a JavaScript port of FFmpeg and provides comprehensive media processing capabilities.
Example: Extracting metadata with ffmpeg.js
You can use ffmpeg.js to get detailed metadata, including codec, format, bitrate, and more.
<input type="file" id="fileInput" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/ffmpeg.js/0.10.0/ffmpeg.min.js"></script>
<script>
  document.getElementById("fileInput").addEventListener("change", function(event) {
    const file = event.target.files[0];
    if (file) {
      const reader = new FileReader();
      reader.onloadend = function() {
        const arrayBuffer = reader.result;
        const ffmpeg = createFFmpeg({ log: true });

        ffmpeg.load().then(() => {
          // Write the input file into ffmpeg.js virtual filesystem
          ffmpeg.FS("writeFile", file.name, new Uint8Array(arrayBuffer));

          // Run ffprobe to get detailed metadata
          ffmpeg.run("-i", file.name).then(() => {
            const metadata = ffmpeg.FS("readFile", file.name + ".ffprobe.json");
            const metadataStr = new TextDecoder().decode(metadata);
            const metadataJson = JSON.parse(metadataStr);

            // Display or process the metadata
            console.log("Metadata:", metadataJson);
          });
        });
      };
      reader.readAsArrayBuffer(file);
    }
  });
</script>
Explanation:
ffmpeg.js allows you to access detailed metadata (e.g., codec, bitrate, etc.) using FFmpeg commands, like -i, which can retrieve information about the file.
You can use ffprobe with ffmpeg.js to extract detailed metadata (using commands like -i for input info). In this example, we assume the ffprobe output is returned as a JSON object, which you can then parse and process.

3. Using the audio-metadata Library (Node.js)

If you're working in a Node.js environment, you can use a package like audio-metadata to extract metadata from audio files.
Installation: npm install audio-metadata
Example: Extracting Audio Metadata with audio-metadata
const fs = require("fs");
const path = require("path");
const { parseFile } = require("audio-metadata");

const filePath = path.join(__dirname, "example.mp3");

fs.readFile(filePath, (err, buffer) => {
  if (err) {
    console.error("Error reading file:", err);
    return;
  }

  const metadata = parseFile(buffer);

  // Display metadata
  console.log("Metadata:", metadata);
});
Explanation:
audio-metadata is used to read the metadata of audio files in Node.js.
The parseFile function reads the audio file and returns metadata, which can include bitrate, duration, sample rate, channels, etc.

4. Using mediainfo.js for Detailed Media Info (Node.js and Browser)

mediainfo.js is a wrapper for MediaInfo, a tool for extracting metadata from media files. This tool can give very detailed information about both audio and video files, including codec, bitrate, resolution, and more.
Installation: npm install mediainfo.js
Example: Extracting Metadata with mediainfo.js
const fs = require('fs');
const MediaInfo = require('mediainfo.js');

MediaInfo({ format: 'object' }).then(function (MediaInfo) {
  const filePath = 'example.mp4';
  fs.readFile(filePath, (err, buffer) => {
    if (err) throw err;
    
    MediaInfo.parseData(buffer).then(data => {
      console.log('File Metadata:', data);
    });
  });
});
Explanation:
mediainfo.js can parse a wide range of media files and provide detailed metadata.
It works both in Node.js and the browser, but may require specific handling for browser environments due to file reading limitations.

Conclusion

  1. Basic Metadata: You can use the built-in HTMLMediaElement in the browser to extract basic metadata such as duration, width, and height.
  2. Advanced Metadata: For more detailed metadata (e.g., codec, bitrate), you can use ffmpeg.js, mediainfo.js, or audio-metadata in Node.js.
  3. Browser Support: ffmpeg.js and mediainfo.js can also be used in the browser for more complex metadata extraction, though they may require larger file handling and more processing time.
These methods will allow you to extract and process metadata for audio and video files effectively in JavaScript.

JavaScript Blob object and its slice method

The slice() method in JavaScript can be used on a Blob object to create a new Blob representing a portion of the original Blob. It is used to extract a portion of the binary data from the Blob object, which could represent an image, video, or audio file. However, it does not modify the content of the media directly in the form of cropping or trimming. The slice() method on a Blob is not used to crop or edit media files like images, videos, or audio (e.g., removing parts of a video, trimming an audio clip, or cropping an image). 

You can use slice() to extract a chunk of image, audio, or video binary data. This is useful when you only need a portion of the file for processing or upload. For instance:
  • Extract the first few bytes of an image file's binary data.
  • Extract a segment of an audio file, but it would still need to be processed as an audio file, not just raw data.
Note: slice() does not modify the media content in terms of resolution, duration, or trimming. To crop, trim, or edit media files, you need specific tools or libraries designed for media manipulation. Look at the following pieces of code:
// Assume 'audioBlob' is a Blob containing audio data
const audioBlob = new Blob([audioData], { type: 'audio/wav' });

// Slice a segment (start from byte 100, end at byte 1000)
const slicedAudioBlob = audioBlob.slice(100, 1000);

// Extracts a portion of the audio's raw binary data
const reader = new FileReader();
reader.onloadend = () => {
  // Use this binary data (you would still need to decode it into audio format)
  console.log(reader.result);
};
reader.readAsArrayBuffer(slicedAudioBlob);
Video Example (not trimming, just extracting binary data):


// Assume 'videoBlob' is a Blob containing video data
const videoBlob = new Blob([videoData], { type: 'video/mp4' });

// Slice a segment (start from byte 100, end at byte 2000)
const slicedVideoBlob = videoBlob.slice(100, 2000);

// it just extracts a portion of the binary data
const reader = new FileReader();
reader.onloadend = () => {
  // Use this binary data (you would still need to decode it into a video format)
  console.log(reader.result);
};
reader.readAsArrayBuffer(slicedVideoBlob);
Here are a few examples of how to use the slice() method with a Blob object:

Example 1: Basic Use of slice() to Create a New Blob

// Create a Blob with some data
const blob = new Blob(['Hello, world!'], { type: 'text/plain' });

// Slice the Blob to get a portion of it
const slicedBlob = blob.slice(0, 5);

// Log the sliced Blob and its content
console.log(slicedBlob);  // Blob { size: 5, type: "text/plain" }

// Create a FileReader to read the sliced Blob
const reader = new FileReader();
reader.onloadend = () => {
  console.log(reader.result);  // "Hello"
};
reader.readAsText(slicedBlob);

Example 2: Using slice() with Start and End Byte Ranges

// Create a Blob with some data
const blob = new Blob(['The quick brown fox jumps over the lazy dog.'], { type: 'text/plain' });

// Slice the Blob from byte 4 to byte 19
const slicedBlob = blob.slice(4, 19);

// Log the sliced Blob and its content
console.log(slicedBlob);  // Blob { size: 15, type: "text/plain" }

// Read the sliced Blob using FileReader
const reader = new FileReader();
reader.onloadend = () => {
  console.log(reader.result);  // "quick brown f"
};
reader.readAsText(slicedBlob);

Example 3: Using slice() with a Specified MIME Type

// Create a Blob with some data
const blob = new Blob(['Hello, this is an image!'], { type: 'text/plain' });

// Slice the Blob and change the MIME type to 'image/png'
const imageBlob = blob.slice(0, 11, 'image/png');

// Log the sliced Blob and its MIME type
console.log(imageBlob);  // Blob { size: 11, type: "image/png" }
In these examples:
  1. The slice(start, end, type) method creates a new Blob containing a portion of the original Blob. The start is the starting byte index, and end is the ending byte index.
  2. The optional type parameter specifies the MIME type for the new Blob.
  3. The slice() method allows you to manipulate binary data or text efficiently by extracting parts of a Blob.

Example 4: Using slice() with an Image Blob

// Create an image Blob (for example, a PNG image)
// (In practice, this could be an image fetched from a server or a file input)
const imageBlob = new Blob([new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10])], { type: 'image/png' });

// Slice the Blob to get a portion of the image data
const slicedImageBlob = imageBlob.slice(0, 4);  // Get the first 4 bytes

// Log the sliced Blob and its size
console.log(slicedImageBlob);  // Blob { size: 4, type: "image/png" }

// Read the sliced Blob as an array buffer (binary data)
const reader = new FileReader();
reader.onloadend = () => {
  console.log(new Uint8Array(reader.result));  // Uint8Array with first 4 bytes of the PNG header
};
reader.readAsArrayBuffer(slicedImageBlob);
In this example:
  1. We create a Blob representing part of an image (image/png type).
  2. We slice the first 4 bytes of the image data, which correspond to the beginning of a PNG file signature (header).
  3. We use FileReader.readAsArrayBuffer() to read the sliced image as binary data.

Example 5: Using slice() with an Audio Blob

// Create an audio Blob (e.g., a WAV file or raw audio data)
const audioBlob = new Blob([new Uint8Array([82, 73, 70, 70, 26, 0, 0, 0])], { type: 'audio/wav' });

// Slice the audio Blob to get a portion of the audio data
const slicedAudioBlob = audioBlob.slice(0, 6);  // Get the first 6 bytes

// Log the sliced audio Blob and its size
console.log(slicedAudioBlob);  // Blob { size: 6, type: "audio/wav" }

// Read the sliced Blob as an array buffer
const reader = new FileReader();
reader.onloadend = () => {
  console.log(new Uint8Array(reader.result));  // Uint8Array with the first 6 bytes of the WAV header
};
reader.readAsArrayBuffer(slicedAudioBlob);
In this example:
  1. We create a Blob representing part of an audio file (e.g., audio/wav type).
  2. We slice the first 6 bytes of the audio data, which may represent part of the header of a WAV file.
  3. We use FileReader.readAsArrayBuffer() to read the sliced audio as binary data.

Example 6: Using slice() with Video Data

// Create a Blob representing a segment of video data (e.g., MP4)
const videoBlob = new Blob([new Uint8Array([0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70])], { type: 'video/mp4' });

// Slice the video Blob to get a portion of the data
const slicedVideoBlob = videoBlob.slice(0, 8);  // Get the first 8 bytes

// Log the sliced Blob and its size
console.log(slicedVideoBlob);  // Blob { size: 8, type: "video/mp4" }

// Read the sliced Blob as an array buffer
const reader = new FileReader();
reader.onloadend = () => {
  console.log(new Uint8Array(reader.result));  // Uint8Array with the first 8 bytes of the video header
};
reader.readAsArrayBuffer(slicedVideoBlob);
In this example:
  1. We create a Blob representing part of a video file (e.g., video/mp4 type).
  2. We slice the first 8 bytes of the video data, which could be the start of an MP4 container format.
  3. We use FileReader.readAsArrayBuffer() to read the sliced video as binary data.

Key Points

  1. The slice() method is useful for extracting parts of binary files, like images, audio, and video, as well as for working with other binary data types.
  2. The slice(start, end, type) method extracts the data between the start and end byte indices and can optionally change the MIME type of the resulting Blob.
  3. Using FileReader.readAsArrayBuffer() or readAsDataURL() can help you process the sliced Blob data depending on your needs (binary data vs. base64 encoding)

How can we decide only the header portion of the audio or video blob is sliced?
To ensure that only the header portion of an audio or video Blob is sliced, you need to understand the structure of the media file format (e.g., WAV for audio, MP4 for video) and determine the length of the header before using slice().

1. Understanding Media Headers

  • Each media file format has a specific header structure that contains metadata about the file, such as the format, codec, duration, and other properties. For instance:
  • WAV audio files start with a header that is typically 44 bytes long (though this can vary slightly depending on the format).
  • MP4 video files have more complex headers that contain information about the video, audio streams, and container format. The header portion is generally at the start of the file, but it's not a fixed size like in WAV.

2. Using slice() to Extract the Header

To extract only the header from an audio or video Blob, you can use the slice() method with an appropriate start and end index, based on your understanding of the file format.

Example 1: Slicing the Header of a WAV Audio File

For a standard WAV file, the header is usually 44 bytes long. So, you can slice the first 44 bytes to get the header portion.
// Assume we have a Blob representing a WAV file
const audioBlob = new Blob([wavFileData], { type: 'audio/wav' });

// Slice the first 44 bytes (WAV header length)
const audioHeaderBlob = audioBlob.slice(0, 44);

// Read the header data as an array buffer
const reader = new FileReader();
reader.onloadend = () => {
  console.log(new Uint8Array(reader.result));  // The header bytes of the WAV file
};
reader.readAsArrayBuffer(audioHeaderBlob);

Example 2: Slicing the Header of an MP4 Video File

An MP4 file's header is not as simple as a WAV file, and it varies depending on the structure of the file. However, for basic cases, you can start by slicing the first few hundred bytes to get the initial portion, which contains the file type and metadata information. If you know the exact structure, you can slice the appropriate range.
// Assume we have a Blob representing an MP4 video file
const videoBlob = new Blob([mp4FileData], { type: 'video/mp4' });

// Slice the first 128 bytes (a common starting range for MP4 headers)
const videoHeaderBlob = videoBlob.slice(0, 128);

// Read the header data as an array buffer
const reader = new FileReader();
reader.onloadend = () => {
  console.log(new Uint8Array(reader.result));  // The header bytes of the MP4 file
};
reader.readAsArrayBuffer(videoHeaderBlob);

3. Determining the Correct Header Length

  • To decide how much of the Blob to slice for the header, you need to:
  • Know the file format: Each audio/video format has its own header length and structure.
  • Consult format specifications: Look up the format specification (e.g., WAV, MP4) to determine the header size.
  • WAV: Typically 44 bytes, though the size may vary slightly based on the specific encoding or metadata.
  • MP4: Headers are more complex and can vary in size, requiring a deeper understanding of the container structure.

4. Practical Approach

If you're working with a variety of media files and are unsure about the exact header length, a practical approach might involve:
  • Inspecting the first few hundred bytes of the Blob to get the file's format and metadata (e.g., using FileReader.readAsArrayBuffer()).
  • Using a media library to parse the media file format, which can automatically identify and extract the header portion.

Example Using a Media Parsing Library

For a more advanced use case, you could use libraries like ffmpeg.js or mp4box.js to parse the header of video or audio files. These libraries can help you decode the media file's metadata and extract only the header portion without needing to manually calculate the size.

Conclusion

To slice only the header of an audio or video Blob, you need to:
  1. Understand the format of the file and its header structure.
  2. Use the slice() method to extract the first n bytes where n corresponds to the header size.
  3. For more complex media formats (like MP4), you might need to inspect the structure or use a library to parse the file.
Related Post: What is Blob

JavaScript, Blob (Binary Large Object)

In JavaScript, a Blob (Binary Large Object) is a data type used to store immutable, raw data. It represents a chunk of binary data that can be read as text, or binary data, or converted to other formats such as Base64.

Key Features of a Blob

  1. Raw Data: It holds raw binary data, such as images, audio, or video files.
  2. Immutable: Once created, the content of a Blob cannot be altered. However, new Blobs can be created from existing ones.
  3. MIME Type: Each Blob has an associated MIME type, which indicates the type of data it contains (e.g., "text/plain", "image/png").

Creating a Blob

You can create a Blob using the Blob constructor:
const blob = new Blob([data], { type: 'mime-type' });
Parameters:
  • data: An array of strings, ArrayBuffer, or other Blob objects.
  • type: Optional. Specifies the MIME type of the data (e.g., "text/plain", "application/json").

Examples

1. Creating a Text Blob
const textBlob = new Blob(["Hello, Blob!"], { type: "text/plain" });
console.log(textBlob);
2. Creating an Image Blob
const binaryData = new Uint8Array([137, 80, 78, 71]); // PNG header
const imageBlob = new Blob([binaryData], { type: "image/png" });
console.log(imageBlob);

Common Operations with Blobs


1. Creating Object URLs
You can create a URL for a Blob using URL.createObjectURL, which can be used to download the Blob or display it.
const blob = new Blob(["Hello, download me!"], { type: "text/plain" });
const url = URL.createObjectURL(blob);
// Create a link to download the Blob
const link = document.createElement("a");
link.href = url;
link.download = "example.txt";
link.click();

// Revoke the object URL after use
URL.revokeObjectURL(url);
2. Reading Blob Content
You can use a FileReader or streams to read the contents of a Blob.
const blob = new Blob(["Hello, Blob!"], { type: "text/plain" });
const reader = new FileReader();

reader.onload = function () {
  console.log(reader.result); // Outputs: Hello, Blob!
};

reader.readAsText(blob);
Example with fetch:
const blob = new Blob(["Hello, Blob!"], { type: "text/plain" });
fetch(URL.createObjectURL(blob))
  .then((response) => response.text())
  .then((text) => console.log(text)); // Outputs: Hello, Blob!

Use Cases

  1. File Uploads: Use Blobs to send binary data to servers via FormData or fetch.
  2. Downloading Files: Create downloadable files in the browser.
  3. Displaying Media: Create object URLs for images, audio, or video for display.
  4. Manipulating Media: Generate or modify media files dynamically.
Blobs are fundamental for handling binary data in web applications, enabling file manipulation, streaming, and more.

Javascript Memoization vs Caching

Memoization and caching are closely related concepts in computer science that focus on storing results of computations to optimize performance. Although they are closely related concepts and sometimes used interchangeably.

Memoization

Memoization is an optimization technique in which the results of expensive function calls are stored (or "memoized") so they can be reused when the same inputs occur again. It's purpose is to avoid redundant computations by remembering the results of previous computations. It is usually specific to a particular function or algorithm.
  1. A specific type of caching where the result of a function call is stored based on its input arguments.
  2. The primary goal is to avoid recomputation by returning the stored result when the same inputs are used again.
  3. It's typically used in the context of pure functions (functions without side effects).
  4. Memoization is usually implemented at the function level to optimize function calls.

JavaScript Example of Memoization

function fibonacci(n, memo = {}) {
  if (n <= 1) return n;

  if (memo[n]) return memo[n]; // Return stored result

  memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo); // Store result
  return memo[n];
}

console.log(fibonacci(10)); // Computes and stores intermediate results

Caching

Caching is a general-purpose performance optimization strategy where data or results are stored temporarily to serve future requests faster. Its purpose is to reduce the cost of fetching or recomputing data by keeping frequently or recently accessed data readily available. Its scope is broader; it is not limited to individual functions. It can apply to web responses (e.g., HTTP caching), file system data and database queries.
  1. A broader concept that involves storing data for future reuse, regardless of its origin.
  2. It can apply to any type of data (e.g., HTTP responses, database queries, or computed results).
  3. It's not limited to function results or arguments.
  4. Caching is broader and can be applied at the application level, like storing API responses or assets in a browser.

Different Types of Caching:

  1. In-Memory Cache: Stores data in RAM for fast access.
  2. Disk Cache: Saves data on disk for persistence.
  3. Distributed Cache: Shared cache system across multiple servers (e.g., Redis, Memcached).

JavaScript Example of Caching

// Simple API response caching
const apiCache = new Map();

async function fetchData(url) {
  if (apiCache.has(url)) {
    console.log("Returning cached data");
    return apiCache.get(url); // Return cached data
  }

  console.log("Fetching new data");
  const response = await fetch(url);
  const data = await response.json();

  apiCache.set(url, data); // Store response in cache
  return data; 
  fetchData("https://api.example.com/data"); // First fetch stores data
  fetchData("https://api.example.com/data"); // Subsequent fetch returns cached data
}

Comparisons


Aspect

Memoization

Caching

Focus

Function results based on inputs

Any reusable data (e.g., API responses, files)

Granularity

Narrow (function-specific)

Broad (application-wide)

Purpose

Avoid recomputation

Optimize data retrieval and reuse

Implementation

Input-output mapping

Key-value storage for general data

Scope

Typically function-specific

Broader application-level


Summary

  • Memoization is a form of caching, but it's specifically for storing function results based on inputs.
  • Caching is a more general concept that applies to storing any reusable data, not just function results.

Thursday, November 14, 2024

JavaScript IIFE Immediately Invoked Function Expression

An Immediately Invoked Function Expression (IIFE) in JavaScript is a function that is executed immediately after it’s defined. IIFEs are commonly used to create a private scope, helping avoid polluting the global scope. There are several ways to create an IIFE in JavaScript. Here are the most common approaches:

1. Using Parentheses Around the Function
This is the classic and most common syntax for an IIFE.
(function() {
  console.log("IIFE using parentheses around function");
})();
2. Using Parentheses After the Function
In this style, parentheses are placed immediately after the function expression without wrapping the function itself in parentheses.
(function() {
  console.log("IIFE using parentheses after function");
}());
3. Using an Arrow Function
With ES6, arrow functions can also be used for IIFEs.
(() => {
  console.log("IIFE using arrow function");
})();
4. Using the + Operator Before the Function
You can use operators like +, -, ~, or ! to make JavaScript treat the function as an expression and invoke it immediately.
+function() {
  console.log("IIFE using + operator");
}();
5. Using the ! Operator Before the Function
Similarly, the ! operator can be used to create an IIFE.
!function() {
  console.log("IIFE using ! operator");
}();
6. Using the void Operator
The void operator forces the function to be evaluated as an expression and ignores its return value.
void function() {
  console.log("IIFE using void operator");
}();
7. With an Async Function
You can also create an IIFE with an async function, which can be useful for immediately invoking code that uses await.
(async function() {
  console.log("IIFE with async function");
  // await someAsyncOperation();
})();
8. With an Arrow Async Function
This combines an arrow function with async, useful in modern JavaScript.
(async () => {
  console.log("IIFE with async arrow function");
  // await someAsyncOperation();
})();

Summary

The most commonly used IIFE syntax is:
  • (function() { ... })();
  • (function() { ... }());
  • However, other variations (+, -, !, void) may also be seen, especially in legacy code or when creating a specific effect like ignoring the return value (void).

JavaScript DOM Core, DOM HTML, DOM CSS, DOM Events, and DOM XML

W3C categorizes DOM into five parts. DOM Core, DOM HTML, DOM CSS, DOM Events, and DOM XML. Explain about each of them.

The Document Object Model (DOM) is a programming interface for web documents. It represents the structure of a document as a tree of objects, where each object corresponds to a part of the document, such as an element, attribute, or text. The W3C categorizes the DOM into five parts, each with specific roles and responsibilities:

1. DOM Core

The DOM Core is the foundation of the DOM specification. It defines the basic methods and properties for interacting with the document structure, such as nodes, elements, and attributes. This part of the DOM is independent of the document type and focuses on how to access and manipulate the tree of elements in a document.

Key features:
  1. Access to nodes in the document.
  2. Operations to traverse the document tree.
  3. Methods for modifying the structure, such as adding, removing, or altering nodes.
  4. Supports both tree and text-based document manipulation.

2. DOM HTML

The DOM HTML extends the DOM Core to provide specific methods and properties for working with HTML documents. It provides representations of HTML elements and offers convenient methods for accessing and manipulating them. This part of the DOM is tailored for HTML documents, reflecting their specific structure and behavior.

Key features:
  1. Access to HTML-specific elements, such as &lt;div&gt;, &lt;button&gt;, &lt;img&gt;, etc.
  2. Specialized methods for interacting with HTML elements like getElementById(), getElementsByClassName(), and getElementsByTagName().
  3. Attributes like innerHTML and outerHTML for working with HTML content.
  4. Handling forms, buttons, and other HTML-specific controls.

3. DOM CSS

The DOM CSS defines methods for interacting with the styles (CSS) of a document. It allows JavaScript to access and modify the CSS properties applied to elements dynamically. This part of the DOM lets developers read and change CSS rules applied to elements in real time.

Key features:
  1. Access to the computed styles of an element (e.g., getComputedStyle()).
  2. Methods for adding, removing, or modifying inline styles of elements (e.g., style.property = value).
  3. Dynamic changes to the CSS rules of an element via JavaScript.

4. DOM Events

The DOM Events specification provides the interface for handling events in the document. It defines how events (such as clicks, key presses, or mouse movements) are triggered and how event handlers are attached to elements. This part of the DOM is crucial for interactive web applications.

Key features:
  1. Event handling mechanisms (e.g., addEventListener(), removeEventListener()).
  2. Event objects that provide details about the event (e.g., mouse position, key pressed).
  3. Event propagation models: bubbling and capturing.
  4. Support for different types of events like mouse, keyboard, form, and media events.

5. DOM XML

The DOM XML extends the DOM Core for working with XML documents. It provides methods for navigating, reading, and modifying XML content. Since XML documents are not tied to any specific language or format (unlike HTML), the XML DOM allows interaction with any structured XML data.

Key features:
  1. Methods for parsing and manipulating XML documents (getElementsByTagName(), getAttribute()).
  2. Ability to create and modify XML elements and attributes.
  3. Support for loading, saving, and serializing XML data.
  4. Works in conjunction with XML parsers to handle XML document trees.
In summary, these five parts of the DOM (Core, HTML, CSS, Events, and XML) enable developers to interact with web documents in various ways, from accessing the structure and styles of HTML content to responding to user interactions and working with XML data.

JavaScript Runtime and client Web APIs

JavaScript Runtime or Execution Environment can access browser objects such as Windows, Navigator, History, Screen, etc., although they are not part of JavaScript because these objects are part of the Browser's Web API (Application Programming Interface). 

The Web API is a collection of JavaScript interfaces that provide access to various browser functionalities and system resources. These APIs are provided by the browser to allow JavaScript running in the browser to interact with the environment and the user's device in a controlled way.

How JavaScript Can Access Browser Objects


JavaScript Runtime Environment: The JavaScript engine (e.g., V8 in Chrome, SpiderMonkey in Firefox) executes JavaScript code. This engine is embedded within the browser and provides the core features of the language (such as variables, functions, control structures, etc.).

Browser's Web API: In addition to the JavaScript engine, modern web browsers provide a set of Web APIs that expose browser-specific functionality. These APIs are not part of the core JavaScript language itself but are made available to JavaScript running in the browser. These browser objects are part of the global environment, making them accessible to JavaScript code.

The Web APIs provide an interface for the JavaScript runtime to access browser-specific capabilities like:
  1. Window Management (window object)
  2. Navigating History (history object)
  3. Browser Information (navigator object)
  4. Screen Information (screen object)
  5. Document and DOM Manipulation (via document object)
  6. Network requests (via fetch, XMLHttpRequest, etc.)
  7. Local Storage (via localStorage, sessionStorage)
  8. Global Object (window):
In the browser environment, the global object is the window object. This means that top-level variables and functions in the JavaScript environment are properties of the window object.For example, when you write alert(), it is actually a shorthand for window.alert(). Similarly, objects like navigator, history, and screen are available as properties of the window object.

Examples

Window Object:
console.log(window.innerWidth); // Accesses the width of the browser window
console.log(window.document);   // Accesses the DOM document object
Navigator Object:
console.log(navigator.userAgent); // Accesses the browser's user-agent string
console.log(navigator.language);  // Accesses the preferred language of the browser
History Object:
history.back();  // Goes back to the previous page in the browsing history
Screen Object:

console.log(screen.width);  // Accesses the screen width of the user's device
console.log(screen.height); // Accesses the screen height of the user's device

How is it Possible?

The JavaScript runtime (engine) running in the browser is aware of the browser environment, as it is hosted within the browser. The Web API is integrated into the browser's JavaScript environment. This means that while JavaScript itself does not define these objects, they are exposed to the global environment by the browser as part of its system capabilities.

Browser-Integrated APIs: These APIs are essentially extensions to JavaScript that allow you to interact with browser-specific features like the DOM, events, storage, navigation, etc. These APIs are not part of the ECMAScript standard but are included by the browser as a service for web developers.

Key Points:
  1. JavaScript Engine (V8, SpiderMonkey, etc.) executes JavaScript.
  2. Web APIs are provided by the browser and can be accessed via JavaScript running in the browser.
  3. These Web APIs are part of the global execution environment (e.g., window object), making them available to JavaScript code.

Summary

JavaScript running in the browser environment can access browser-specific objects such as window, navigator, history, and screen because these objects are part of the browser's Web API. The JavaScript engine is integrated into the browser, which allows it to expose these APIs to JavaScript code running on web pages. These APIs provide a bridge between JavaScript and the underlying browser and system resources, enabling web developers to create interactive and dynamic web applications.


JavaScript Pseudo URL, Different Techniques, its Pros and Cons

A pseudo URL refers to a URL that is not a typical web address pointing to a resource on the web. Instead, it is a special kind of URL that executes JavaScript code when invoked. The most common example of a JavaScript pseudo URL is JavaScript :. This pseudo protocol allows you to embed JavaScript directly within a hyperlink (<a> tag) in HTML, causing the script to run when the link is clicked.

Example:
<a href="JavaScript:alert('Hello, World!')">Click Me</a>
In the example above, when the link is clicked, the alert('Hello, World!') JavaScript code is executed. The href attribute uses the JavaScript: pseudo protocol, which indicates that the browser should execute the JavaScript code directly rather than navigating to a different URL.

Different ways to write JavaScript Pseudo URL

In JavaScript, pseudo URLs are often used to create hyperlinks or triggers that execute JavaScript code directly without navigating to a new URL. Here are different ways to write JavaScript pseudo URLs:

1. Basic javascript: URL Scheme
This is the simplest and most common form of JavaScript pseudo URL, typically used in the href attribute of <a> tags.
<a href="javascript:alert('Hello!');">Click Me</a>
2. Using void(0) to Prevent Page Navigation
If you want to execute JavaScript code without triggering any page navigation, you can use void(0), which ensures no new page load or history entry.
<a href="javascript:void(0);" onclick="alert('Hello!');">Click Me</a>
This approach makes it easier to separate the URL and the actual JavaScript code to be executed (placed in onclick).

3. Using an Empty Function javascript:;
Using javascript:; can act as a pseudo URL with no page navigation or action. This is often used if JavaScript functions are added as event listeners or inline.
<a href="javascript:;" onclick="alert('Hello!');">Click Me</a>
4. Wrapping the Code in an IIFE (Immediately Invoked Function Expression)
An IIFE in a JavaScript URL can allow more complex operations to run without polluting the global scope.
<a href="javascript:(function(){ alert('Hello!'); })();">Click Me</a>
5. Setting href="#" with onclick
Using # as the href and defining the JavaScript in an onclick attribute is another common way to trigger JavaScript, but it can lead to page scrolling to the top unless you add return false; to prevent the default action.
<a href="#" onclick="alert('Hello!'); return false;">Click Me</a>
6. Using javascript:document.location to Change the URL
You can also use javascript:document.location to change the URL, which can help with dynamic redirects.
<a href="javascript:document.location='https://example.com';">Go to Example.com</a>

Pros of Using JavaScript Pseudo URL

  1. Quick and Simple: The : pseudo URL allows developers to embed small JavaScript snippets directly into HTML, making it easy for quick tasks like showing alerts or manipulating page elements without needing to write separate JavaScript code in <script> tags or external files.
  2. No Need for External Files: It can eliminate the need for creating separate external JavaScript files for small, one-off scripts, which can be convenient for small applications or demos.
  3. Easy for Prototyping: During rapid prototyping or debugging, developers may use JavaScript pseudo URLs to quickly test code snippets or add temporary functionality.

Cons and Why It is Discouraged to Use:

While it might seem convenient, using : pseudo URLs is generally discouraged due to the following reasons:

Poor Separation of Concerns: 
  • Mixing HTML and JavaScript: It breaks the principle of separating structure (HTML), style (CSS), and behavior (JavaScript). By embedding JavaScript in the href attribute, you couple the behavior (JavaScript) tightly with the content (HTML), making the code harder to maintain.
  • This violates Separation of Concerns, which is a core principle of modern web development. It's better practice to use external JavaScript files or event listeners to handle behavior.
Security Risks (Cross-Site Scripting - XSS):
  • URLs can expose your site to XSS (Cross-Site Scripting) vulnerabilities. Malicious actors can inject JavaScript code into these URLs to exploit users, especially if input is not properly sanitized.
  • If user input or data from external sources is directly placed in : links, this can open up potential for malicious JavaScript execution.
Accessibility Issues:
  • Screen readers and assistive technologies may not handle : links properly. Users with disabilities who rely on these tools might not be able to interact with the site or understand the purpose of the link.
  • In cases where JavaScript is disabled (e.g., in older browsers or specific settings), the : pseudo URL will not work, which can degrade the user experience.
SEO (Search Engine Optimization) Issues:
  • URLs are not crawlable by search engines. They don’t represent actual resources on the web, so search engines can’t index or follow them. This means that important links might be skipped by search engine crawlers.
Inconsistent Behavior Across Browsers:
  • Some older browsers or non-standard browsers might not support : pseudo URLs properly, leading to inconsistent behavior for users across different platforms.
  • Also, some browser extensions or security software might block or warn users when they encounter links that use the : pseudo URL.
User Experience (UX) Concerns:
  • If a user expects to navigate to another page but clicks a : link, they might be confused when nothing happens or when a popup appears. This can lead to frustration and a poor user experience.
Why It Is Discouraged to Use:
Due to the security risks, poor maintainability, and potential accessibility issues, pseudo URLs (like :) are discouraged in modern web development. Instead, developers are encouraged to:
  1. Use event listeners (e.g., addEventListener) to separate JavaScript behavior from HTML.
  2. Use JavaScript in external files for better modularity, readability, and reusability.
  3. Leverage HTML attributes like id and class to trigger JavaScript via event listeners, rather than embedding JavaScript directly within HTML elements.
Recommended Alternatives to JavaScript Pseudo URLs:
  • Event Listeners: Instead of using : in an href or onclick attribute, you can add an event listener to an element to handle user interactions.
Example:
<button id="myButton">Click Me</button>

<script>
  document.getElementById("myButton").addEventListener("click", function() {
    alert('Hello, World!');
  });
</script>
  • Links for Navigation: If the link is intended for navigation, ensure that the href points to an actual resource (a URL), and use JavaScript for behavior (if needed) separately.
Example:
<a href="https://www.example.com">Visit Example</a>

Conclusion

While the : pseudo URL can be convenient for small tasks, it is considered a poor practice due to the security risks, lack of maintainability, accessibility issues, and poor user experience. It’s better to use event listeners and external JavaScript files to ensure a clean, secure, and maintainable separation between HTML and JavaScript.

Tuesday, November 12, 2024

JavaScript Return Statement With or Without Argument

In JavaScript, the return statement is used to exit a function and optionally return a value to the caller. There are differences between using a return statement with or without an argument.

1. Return with an argument

When a return statement is used with an argument, it returns the specified value to the function caller.

Example:
function add(a, b) {
  return a + b; // returns the sum of a and b
}

let result = add(5, 3); 
console.log(result); // Output: 8
In this case, return a + b; sends the result of the expression (a + b) back to the caller.
The function add returns a specific value, and it can be assigned to a variable (result) or used directly.

2. Return without an argument

When the return statement is used without an argument, it returns undefined by default. This means that the function ends without explicitly returning any value.

Example:
function greet(name) {
  console.log('Hello, ' + name);
  return; // returns undefined by default
}

let greeting = greet('Alice');
console.log(greeting); // Output: undefined
In this case, the greet function performs an action (printing a message) but does not return any value.
The return; statement is implicit and returns undefined, which is the default return value when no value is provided.

3. Without return statement

When return statement is not used in function, then it implicitly returns undefined similar to case2.

Summary

  1. With an argument: The function returns the value provided as the argument to the return statement.
  2. Without an argument: The function exits and implicitly returns undefined.



JavaScript, Obtrusive and Non-obtrusive

The distinction between obtrusive JavaScript and non-obtrusive JavaScript relates to how JavaScript is integrated with HTML and the separation of concerns (HTML for structure, CSS for styling, and JavaScript for behavior). Here's a breakdown of the differences:

1. Obtrusive JavaScript:

Obtrusive JavaScript refers to the practice of mixing JavaScript directly with HTML elements, typically through inline event handlers or by embedding JavaScript directly into HTML attributes. This approach tends to "pollute" the HTML structure with behavior-related code, making it harder to maintain and less flexible.

Key Characteristics of Obtrusive JavaScript:

  1. Directly in HTML Elements: JavaScript code is placed within HTML elements using event attributes like onclick, onmouseover, onchange, etc.
  2. Tight Coupling: JavaScript is tightly coupled with the HTML markup, making the HTML and JavaScript harder to separate.
  3. Harder to Maintain: If the HTML structure changes, or if you want to update JavaScript behavior, you often need to make changes directly in the HTML.
  4. Lack of Separation of Concerns: Mixing HTML, CSS, and JavaScript together leads to poor separation of concerns, which can result in cluttered code.
Example of Obtrusive JavaScript:
<button onclick="alert('Hello, World!')">Click Me</button>
Here, JavaScript is embedded directly in the onclick attribute of the HTML element, making it obtrusive.

2. Non-obtrusive JavaScript:

Non-obtrusive JavaScript refers to keeping JavaScript separate from the HTML structure. In this approach, JavaScript behavior is applied through event listeners and other JavaScript methods, rather than directly embedding JavaScript code in HTML attributes. The idea is to keep the HTML clean and only include structure, while JavaScript is used purely for behavior.

Key Characteristics of Non-obtrusive JavaScript:

  1. Separation of Concerns: JavaScript is separated from HTML, allowing for cleaner and more maintainable code.
  2. Uses Event Listeners: JavaScript functionality is added via event listeners in JavaScript files, rather than inline event handlers in HTML.
  3. Easier Maintenance and Flexibility: Since JavaScript is kept in separate files or <script> tags, it’s easier to update and maintain. Changes to behavior don’t require modifying HTML.
  4. Improved Accessibility: Non-obtrusive JavaScript enhances accessibility because it allows the HTML to be readable and functional even without JavaScript enabled (progressive enhancement).
Example of Non-obtrusive JavaScript:
<button id="myButton">Click Me</button>

<script>
  document.getElementById("myButton").addEventListener("click", function() {
    alert("Hello, World!");
  });
</script>
Here, JavaScript is separated from the HTML structure. The event listener is added via JavaScript, not inline within the HTML.

Why Non-obtrusive JavaScript is Preferred:

  1. Separation of Concerns: It keeps the HTML (structure), CSS (presentation), and JavaScript (behavior) separate, making each part of the application easier to manage and update.
  2. Maintenance: Non-obtrusive JavaScript allows developers to maintain JavaScript independently from HTML, making it easier to update behavior without touching the HTML structure.
  3. Progressive Enhancement: In the absence of JavaScript, the HTML still functions as a basic page (e.g., a form can still be submitted even if JavaScript fails).
  4. Cleaner Code: It makes the code cleaner and more readable by avoiding inline JavaScript.

Conclusion

  1. Obtrusive JavaScript is when JavaScript is directly embedded in HTML through event attributes, creating a tight coupling between the structure and behavior.
  2. Non-obtrusive JavaScript is when JavaScript behavior is applied through event listeners and external files, promoting better separation of concerns, maintainability, and flexibility.
  3. In modern web development, non-obtrusive JavaScript is generally preferred as it leads to more maintainable, scalable, and accessible applications.

Hot Topics