JavaScript และ IndexedDB

JavaScript และ IndexedDB

ในบทความนี้ เราจะอธิบายเกี่ยวกับ JavaScript และ IndexedDB

บทแนะนำนี้จะอธิบายเกี่ยวกับ JavaScript และ IndexedDB แบบทีละขั้นตอน พร้อมด้วยตัวอย่างโค้ดที่ปฏิบัติได้จริงในแต่ละขั้น เพื่อช่วยให้คุณเข้าใจลึกซึ้งยิ่งขึ้น

YouTube Video

javascript-indexed-db.html
  1<!DOCTYPE html>
  2<html lang="en">
  3<head>
  4  <meta charset="UTF-8">
  5  <title>JavaScript &amp; HTML</title>
  6  <style>
  7    * {
  8        box-sizing: border-box;
  9    }
 10
 11    body {
 12        margin: 0;
 13        padding: 1em;
 14        padding-bottom: 10em;
 15        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
 16        background-color: #f7f9fc;
 17        color: #333;
 18        line-height: 1.6;
 19    }
 20
 21    .container {
 22        max-width: 800px;
 23        margin: 0 auto;
 24        padding: 1em;
 25        background-color: #ffffff;
 26        border: 1px solid #ccc;
 27        border-radius: 10px;
 28        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
 29    }
 30
 31    .container-flex {
 32        display: flex;
 33        flex-wrap: wrap;
 34        gap: 2em;
 35        max-width: 1000px;
 36        margin: 0 auto;
 37        padding: 1em;
 38        background-color: #ffffff;
 39        border: 1px solid #ccc;
 40        border-radius: 10px;
 41        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
 42    }
 43
 44    .left-column, .right-column {
 45        flex: 1 1 200px;
 46        min-width: 200px;
 47    }
 48
 49    h1, h2 {
 50        font-size: 1.2rem;
 51        color: #007bff;
 52        margin-top: 0.5em;
 53        margin-bottom: 0.5em;
 54        border-left: 5px solid #007bff;
 55        padding-left: 0.6em;
 56        background-color: #e9f2ff;
 57    }
 58
 59    button {
 60        display: block;
 61        margin: 1em auto;
 62        padding: 0.75em 1.5em;
 63        font-size: 1rem;
 64        background-color: #007bff;
 65        color: white;
 66        border: none;
 67        border-radius: 6px;
 68        cursor: pointer;
 69        transition: background-color 0.3s ease;
 70    }
 71
 72    button:hover {
 73        background-color: #0056b3;
 74    }
 75
 76    #output {
 77        margin-top: 1em;
 78        background-color: #1e1e1e;
 79        color: #0f0;
 80        padding: 1em;
 81        border-radius: 8px;
 82        min-height: 200px;
 83        font-family: Consolas, monospace;
 84        font-size: 0.95rem;
 85        overflow-y: auto;
 86        white-space: pre-wrap;
 87    }
 88
 89    .highlight {
 90        outline: 3px solid #ffc107; /* yellow border */
 91        background-color: #fff8e1;  /* soft yellow background */
 92        transition: background-color 0.3s ease, outline 0.3s ease;
 93    }
 94
 95    .active {
 96        background-color: #28a745; /* green background */
 97        color: #fff;
 98        box-shadow: 0 0 10px rgba(40, 167, 69, 0.5);
 99        transition: background-color 0.3s ease, box-shadow 0.3s ease;
100    }
101  </style>
102</head>
103<body>
104    <div class="container">
105        <h1>JavaScript Console</h1>
106        <button id="executeBtn">Execute</button>
107        <div id="output"></div>
108    </div>
109
110    <script>
111        // Override console.log to display messages in the #output element
112        (function () {
113            // Override console.log
114            const originalLog = console.log;
115            console.log = function (...args) {
116                originalLog.apply(console, args);
117                const message = document.createElement('div');
118                message.textContent = args
119                    .map(arg => (typeof arg === "object" && arg !== null ? JSON.stringify(arg) : String(arg)))
120                    .join(" ");
121                output.appendChild(message);
122            };
123
124            // Override console.error
125            const originalError = console.error;
126            console.error = function (...args) {
127                originalError.apply(console, args);
128                const message = document.createElement('div');
129                message.textContent = args
130                    .map(arg => (typeof arg === "object" && arg !== null ? JSON.stringify(arg) : String(arg)))
131                    .join(" ");
132                message.style.color = 'red'; // Color error messages red
133                output.appendChild(message);
134            };
135        })();
136
137        document.getElementById('executeBtn').addEventListener('click', () => {
138            // Prevent multiple loads
139            if (document.getElementById('externalScript')) return;
140
141            const script = document.createElement('script');
142            script.src = 'javascript-indexed-db.js';
143            script.id = 'externalScript';
144            //script.onload = () => console.log('javascript-indexed-db.js loaded and executed.');
145            //script.onerror = () => console.log('Failed to load javascript-indexed-db.js.');
146            document.body.appendChild(script);
147        });
148    </script>
149</body>
150</html>

JavaScript และ IndexedDB

IndexedDB เป็นฐานข้อมูล key-value แบบ asynchronous ที่ฝังอยู่ในเบราว์เซอร์ มันมีฟีเจอร์คล้ายกับฐานข้อมูลเชิงสัมพันธ์ และช่วยให้คุณสามารถเก็บและค้นหาข้อมูลขนาดใหญ่แบบมีโครงสร้างบนฝั่งไคลเอนต์ โดยเฉพาะเหมาะสำหรับแอปพลิเคชันที่รองรับออฟไลน์ และ PWAs (Progressive Web Apps)

คุณสมบัติของ IndexedDB

  • ทำงานแบบ asynchronous และ event-driven
  • อ็อบเจกต์ JavaScript สามารถเก็บไว้ใน object stores
  • สามารถค้นหาด้วย query หรือดัชนี (index) ได้
  • มีความจุในการจัดเก็บสูง (หลักร้อย MB ขึ้นไป) สามารถเก็บข้อมูลได้มากกว่าคุกกี้หรือ localStorage

การเปิดและสร้างฐานข้อมูล

ในการใช้ IndexedDB จำเป็นต้องเปิดฐานข้อมูลก่อน ถ้าฐานข้อมูลยังไม่มี จะถูกสร้างโดยอัตโนมัติ

1const request = indexedDB.open('MyDatabase', 1); // Specify DB name and version
  • เมธอด open จะเปิดฐานข้อมูลแบบ asynchronous และทำให้เกิด event 3 อย่างดังนี้

onsuccess

1// Fired when database is successfully opened
2request.onsuccess = (event) => {
3    const db = event.target.result; // Database instance
4    console.log('Database opened successfully:', db.name);
5};
  • event onsuccess จะถูกเรียกเมื่อฐานข้อมูลเปิดสำเร็จ การดำเนินการขั้นต่อไปควรใช้ request.result ซึ่งจะใช้ได้ในขณะนี้

onerror

1// Fired when database fails to open
2request.onerror = (event) => {
3    console.error('Failed to open database:', event.target.error);
4};
  • event onerror จะถูกเรียกเมื่อเปิดฐานข้อมูลล้มเหลว ควรดำเนินการบันทึกข้อผิดพลาดและจัดการข้อผิดพลาดที่นี่

onupgradeneeded

 1// Fired when database is newly created or upgraded
 2request.onupgradeneeded = (event) => {
 3    const db = event.target.result;
 4    console.log('Database upgrade needed (or newly created):', db.name);
 5
 6    // Example: Create an object store (like a table) if it doesn’t exist
 7    if (!db.objectStoreNames.contains('users')) {
 8        db.createObjectStore('users', { keyPath: 'id' });
 9        console.log('Object store "users" created');
10    }
11};
  • event onupgradeneeded จะถูกเรียกเมื่อสร้างฐานข้อมูลใหม่หรือเปลี่ยนเวอร์ชันที่สูงกว่าของเดิม การสร้างตาราง (object stores) และกำหนด schema ควรกระทำในช่วงเวลานี้

การสร้าง Object Store

ขั้นแรก ให้สร้าง 'object store' (เทียบได้กับตาราง) ภายใน onupgradeneeded

1request.onupgradeneeded = function (event) {
2    const db = event.target.result;
3    console.log("onupgradeneeded triggered. Database version:", db.version);
4    if (!db.objectStoreNames.contains('users')) {
5        console.log("Creating object store: users");
6        const store = db.createObjectStore('users', { keyPath: 'id' });
7        store.createIndex('name', 'name', { unique: false });
8    }
9};

ณ จุดนี้ การตั้งค่าต่อไปนี้จะถูกกำหนด:

  • createObjectStore createObjectStore เป็นเมธอดที่ใช้สร้าง object store ใหม่ภายในฐานข้อมูล คุณสามารถเก็บ record ต่าง ๆ ไว้ใน object store และกำหนดวิธีการจัดการข้อมูลผ่าน keyPath และออปชั่นอื่น ๆ กระบวนการนี้ต้องทำภายใน event onupgradeneeded

  • keyPath: 'id' กำหนด keyPath ให้ชี้ไปที่ property id ซึ่งเป็น primary key ที่ใช้ระบุความเป็นเอกลักษณ์ของแต่ละ record การตั้งค่านี้จะทำให้สามารถใช้ id โดยอัตโนมัติเมื่อเพิ่ม ค้นหา หรืออัปเดตข้อมูล

  • createIndex ใช้เมธอด createIndex เพื่อสร้างดัชนี (index) จาก property name สำหรับค้นหา การตั้งค่า unique: false จะอนุญาตให้มี record ที่ name ซ้ำกันได้หลายรายการ

การจัดการเมื่อเชื่อมต่อฐานข้อมูลสำเร็จ

กำหนดกระบวนการที่ต้องดำเนินการเมื่อเชื่อมต่อฐานข้อมูลสำเร็จให้กับ event onsuccess ภายในกระบวนการนี้ คุณจะได้รับอินสแตนซ์ของฐานข้อมูล และเตรียมสำหรับดำเนินการอ่าน/เขียนข้อมูล

1request.onsuccess = function (event) {
2    const db = event.target.result;
3    console.log('Database opened successfully');
4    // Use db for reading and writing in subsequent operations
5};
  • กระบวนการนี้จะทำงานเมื่อเชื่อมต่อฐานข้อมูล IndexedDB สำเร็จ

  • event.target.result จะเก็บอินสแตนซ์ของฐานข้อมูลที่เปิดสำเร็จ (IDBDatabase object) ซึ่งใช้ในการเริ่ม transaction และเข้าถึง object store ต่างๆ

  • กระบวนการอ่านและเขียนจริง เช่น การเพิ่ม ค้นหา อัปเดต และลบข้อมูล จะกระทำโดยใช้วัตถุ db

ในขณะนี้ ฐานข้อมูลพร้อมใช้งานแล้ว คุณสามารถเริ่ม transaction ได้อย่างปลอดภัย

การเพิ่มข้อมูล

วิธีการเพิ่มข้อมูลใหม่ไปยัง IndexedDB มีดังนี้

 1function addUser(db, user) {
 2    const transaction = db.transaction('users', 'readwrite');
 3    const store = transaction.objectStore('users');
 4    const request = store.add(user);
 5
 6    request.onsuccess = () => {
 7        console.log('User added:', user);
 8    };
 9
10    request.onerror = () => {
11        console.error('Add failed:', request.error);
12    };
13}
14
15// Example: Add a user
16request.onsuccess = function (event) {
17    const db = event.target.result;
18    addUser(db, { id: 1, name: 'Alice' });
19    addUser(db, { id: 2, name: 'Bob' });
20    addUser(db, { id: 3, name: 'John' });
21};
  • สร้าง transaction ด้วย db.transaction() ระบุ object store ที่ต้องการดำเนินการ และระบุโหมด (ในกรณีนี้คือ readwrite)
  • เพิ่มข้อมูลใหม่โดยใช้เมธอด store.add()
  • Transaction จะถูก commit โดยอัตโนมัติ แต่หากคุณต้องการรวมหลาย ๆ กระบวนการเข้าด้วยกัน สามารถจัดการได้ผ่าน event หลัง transaction สิ้นสุด

การเรียกดูข้อมูล (ค้นหาด้วย Primary Key)

วิธีการเรียกข้อมูลแต่ละรายการด้วย primary key มีดังนี้

 1function getUserById(db, id) {
 2    const transaction = db.transaction('users', 'readonly');
 3    const store = transaction.objectStore('users');
 4    const request = store.get(id);
 5
 6    request.onsuccess = () => {
 7        if (request.result) {
 8            console.log('User found:', request.result);
 9        } else {
10            console.log('User not found');
11        }
12    };
13
14    request.onerror = () => {
15        console.error('Error retrieving user:', request.error);
16    };
17}
18
19// Example: Get a user by id
20request.onsuccess = function (event) {
21    const db = event.target.result;
22    getUserById(db, 1);
23};
  • สร้าง transaction แบบ read-only ด้วย db.transaction()
  • ใช้ store.get(id) เพื่อดึงข้อมูลที่ตรงกับ primary key ที่ระบุไว้
  • เมื่อดึงข้อมูลสำเร็จ onsuccess จะถูกเรียก และแสดงผลลัพธ์หากมีข้อมูล ถ้าไม่มีผลลัพธ์ จะถือว่า 'ไม่มีข้อมูลที่ตรงกัน'

การค้นหาโดยใช้ Index

หากต้องการค้นหาด้วย property อื่นที่ไม่ใช่ primary key ให้ใช้ index ที่สร้างขึ้นไว้ก่อนหน้า

 1function getUserByName(db, name) {
 2    const transaction = db.transaction('users', 'readonly');
 3    const store = transaction.objectStore('users');
 4    const index = store.index('name');
 5    const request = index.get(name);
 6
 7    request.onsuccess = () => {
 8        if (request.result) {
 9            console.log('User by name:', request.result);
10        } else {
11            console.log('No user found with that name');
12        }
13    };
14
15    request.onerror = () => {
16        console.error('Error retrieving user by name:', request.error);
17    };
18}
19
20// Example: Get a user by id
21request.onsuccess = function (event) {
22    const db = event.target.result;
23    getUserByName(db, 'Alice');
24};
  • เข้าถึง index name ที่สร้างไว้ล่วงหน้าด้วย store.index('name')
  • index.get(value) จะดึง record ตัวแรกที่ค่าตรงกับที่ระบุ ถ้ามี record หลายรายการที่ค่าตรงกัน สามารถดึงทั้งหมดได้ด้วย index.getAll(value)

การอัปเดตข้อมูล

ในการอัปเดต (เขียนทับ) ข้อมูลเดิม ให้ใช้เมธอด put()

หากมี record ที่ primary key ตรงกัน ข้อมูลจะถูกอัปเดต ถ้าไม่มีก็จะเพิ่ม record ใหม่

 1function updateUser(db, updatedUser) {
 2    const transaction = db.transaction('users', 'readwrite');
 3    const store = transaction.objectStore('users');
 4    const request = store.put(updatedUser);
 5
 6    request.onsuccess = () => {
 7        console.log('User updated:', updatedUser);
 8    };
 9
10    request.onerror = () => {
11        console.error('Update failed:', request.error);
12    };
13}
14
15// Example: Update user
16request.onsuccess = async (event) => {
17    const db = event.target.result;
18
19    // Test : update existing user
20    updateUser(db, { id: 3, name: 'John Updated' });
21
22    // Test : insert new user
23    updateUser(db, { id: 4, name: 'Charlie' });
24};
  • put() เป็นเมธอดที่สะดวก เพราะรองรับทั้งการอัปเดตและการเพิ่มข้อมูล
  • ถ้ามีข้อมูลที่ primary key เดียวกันอยู่แล้ว จะถูกเขียนทับ
  • หากต้องการตรวจสอบการมีอยู่ก่อนอัปเดต สามารถใช้ get() เพื่อตรวจสอบล่วงหน้าได้

การลบข้อมูล

ในการลบข้อมูลที่ตรงกับ primary key ให้ใช้เมธอด delete()

 1function deleteUser(db, id) {
 2    const transaction = db.transaction('users', 'readwrite');
 3    const store = transaction.objectStore('users');
 4    const request = store.delete(id);
 5
 6    request.onsuccess = () => {
 7        console.log(`User with id=${id} deleted successfully`);
 8    };
 9
10    request.onerror = () => {
11        console.error(`Failed to delete user with id=${id}:`, request.error);
12    };
13}
14
15// Example: Delete a user by id
16request.onsuccess = function (event) {
17    const db = event.target.result;
18    deleteUser(db, 4);
19};
  • ใช้ store.delete(id) เพื่อลบข้อมูลที่ primary key ตรงกัน
  • โปรดทราบ แม้ว่าข้อมูลจะไม่มีอยู่จริงก็ไม่เกิดข้อผิดพลาด และนับว่าประสบความสำเร็จ
  • การเพิ่มการจัดการข้อผิดพลาดจะช่วยให้โค้ดมีความเสถียรยิ่งขึ้น

การเรียกดูข้อมูลทั้งหมด

getAll()

หากต้องการดึง record ทั้งหมดใน object store ให้ใช้เมธอด getAll()

 1function getAllUsers(db) {
 2    const transaction = db.transaction('users', 'readonly');
 3    const store = transaction.objectStore('users');
 4    const request = store.getAll();
 5
 6    request.onsuccess = () => {
 7        console.log('All users:', request.result);
 8    };
 9
10    request.onerror = () => {
11        console.error('Failed to retrieve users:', request.error);
12    };
13}
14
15// Example: Get all users
16request.onsuccess = function (event) {
17    const db = event.target.result;
18    getAllUsers(db);
19};
  • getAll() จะดึงข้อมูลทุก record ใน object store ที่ระบุมาเป็น array
  • แม้จะดึงข้อมูลจำนวนมากในครั้งเดียวก็จะถูกประมวลผลอย่างมีประสิทธิภาพ
  • ผลลัพธ์จะถูกเก็บในรูปแบบ array ที่ request.result
  • ควรใส่ขั้นตอนสำหรับจัดการข้อผิดพลาดเสมอ เพื่อรองรับกรณีเกิดความล้มเหลว

openCursor()

openCursor() เป็นเมธอดสำหรับ เดินรายการข้อมูลทีละ record ใน object store หรือ index เหมาะกับกรณีที่ต้องการประมวลผลข้อมูลทีละรายการ ไม่ใช่ดึงทั้งหมดในคราวเดียว

 1function getAllUsersWithCursor(db) {
 2    const transaction = db.transaction('users', 'readonly');
 3    const store = transaction.objectStore('users');
 4    const request = store.openCursor();
 5
 6    request.onsuccess = () => {
 7        const cursor = request.result;
 8        if (cursor) {
 9            console.log('User:', cursor.value); // Process the current record
10            cursor.continue(); // Move to the next record
11        } else {
12            console.log('All users have been processed.');
13        }
14    };
15
16    request.onerror = () => {
17        console.error('Failed to open cursor:', request.error);
18    };
19}
20
21// Example: Get all users
22request.onsuccess = function (event) {
23    const db = event.target.result;
24    getAllUsersWithCursor(db);
25};
  • โดยใช้ openCursor() คุณจะเริ่มเปิด cursor และดึงข้อมูลใน object store ทีละรายการ
  • ใช้ cursor.value เพื่อดึงอ็อบเจกต์ข้อมูลของ record ปัจจุบัน
  • ขยับ cursor ไปยัง record ถัดไปด้วย cursor.continue()
  • เมื่อ cursor === null หมายถึงได้เดินครบทุกข้อมูลแล้ว

ตัวอย่างกระบวนการอัปเดตโดยใช้ openCursor()

ตัวอย่างเช่น กระบวนการเปลี่ยนชื่่อผู้ใช้ที่มี name เป็น Alice เป็น Alicia จะมีลักษณะดังนี้:

 1function updateUserName(db, oldName, newName) {
 2    const transaction = db.transaction('users', 'readwrite');
 3    const store = transaction.objectStore('users');
 4    const index = store.index('name'); // Use the 'name' index
 5    const request = index.openCursor(IDBKeyRange.only(oldName));
 6
 7    request.onsuccess = () => {
 8        const cursor = request.result;
 9        if (cursor) {
10            const user = cursor.value;
11            user.name = newName; // Update the name
12            const updateRequest = cursor.update(user);
13
14            updateRequest.onsuccess = () => {
15                console.log('Updated user:', user);
16            };
17
18            updateRequest.onerror = () => {
19                console.error('Failed to update user:', updateRequest.error);
20            };
21
22            cursor.continue();
23        } else {
24            console.log('All matching users have been updated.');
25        }
26    };
27
28    request.onerror = () => {
29        console.error('Cursor error:', request.error);
30    };
31}
32
33// Example: Update user name
34request.onsuccess = function (event) {
35    const db = event.target.result;
36    updateUserName(db, 'Alice', 'Alicia');
37};
  • IDBKeyRange.only(oldName) โดยการใช้ IDBKeyRange.only คุณสามารถเจาะจงเฉพาะเรคคอร์ดที่มีคีย์ตรงกับ oldName ได้ สิ่งนี้มีประโยชน์เมื่อคุณต้องการเข้าถึงค่าที่ระบุโดยตรง

  • cursor.update() หลังจากอัปเดต cursor.value แล้ว การเรียกใช้ update() จะเขียนทับเรคคอร์ดที่เกี่ยวข้อง

  • การจัดการกรณีตรงกันหลายรายการ โดยการเรียกใช้ cursor.continue() คุณสามารถเลื่อนเคอร์เซอร์ไปยังเรคคอร์ดถัดไปที่ตรงกัน สิ่งนี้จะช่วยให้คุณสามารถประมวลผลเรคคอร์ดหลายรายการที่ตรงกับคีย์หรือเงื่อนไขเดียวกันได้อย่างต่อเนื่อง

  • การจัดการข้อผิดพลาด โดยการบันทึก log ใน onerror เมื่อกระบวนการเกิดข้อผิดพลาด จะช่วยให้ง่ายต่อการตรวจสอบและแก้ไขปัญหาระหว่างการทำงาน

ตัวอย่างกระบวนการลบโดยใช้ openCursor()

ตัวอย่างเช่น กระบวนการลบผู้ใช้ทั้งหมดที่มี name เป็น Bob จะมีลักษณะดังนี้:

 1function deleteUsersByName(db, targetName) {
 2    const transaction = db.transaction('users', 'readwrite');
 3    const store = transaction.objectStore('users');
 4    const index = store.index('name');
 5    const request = index.openCursor(IDBKeyRange.only(targetName));
 6
 7    request.onsuccess = () => {
 8        const cursor = request.result;
 9        if (cursor) {
10            const deleteRequest = cursor.delete();
11
12            deleteRequest.onsuccess = () => {
13                console.log('Deleted user:', cursor.value);
14            };
15
16            deleteRequest.onerror = () => {
17                console.error('Failed to delete user:', deleteRequest.error);
18            };
19
20            cursor.continue();
21        } else {
22            console.log('All matching users have been deleted.');
23        }
24    };
25
26    request.onerror = () => {
27        console.error('Cursor error:', request.error);
28    };
29}
30
31// Example: Delete user by name
32request.onsuccess = function (event) {
33    const db = event.target.result;
34    deleteUsersByName(db, 'Bob');
35};
  • cursor.delete() เมื่อใช้ cursor.delete() เรคคอร์ดที่ตำแหน่งปัจจุบันของเคอร์เซอร์จะถูกลบ เนื่องจากผลลัพธ์จะถูกส่งกลับแบบอัสินโครนัส คุณสามารถตรวจสอบกระบวนการใน onsuccess ได้

หมายเหตุเกี่ยวกับธุรกรรมและการประมวลผลแบบอัสินโครนัส

IndexedDB เป็นแบบอัสินโครนัสและทำงานตามเหตุการณ์ (event-driven) ต้องดำเนินการกับทุกฟังก์ชันด้วยเหตุการณ์ onsuccess หรือ onerror เมื่อคุณต้องการจัดกลุ่มกระบวนการหลายรายการเข้าด้วยกัน การนำมาห่อรวมใน Promise จะสะดวกมาก

 1function openDatabase() {
 2    return new Promise((resolve, reject) => {
 3        const request = indexedDB.open('MyDatabase', 1);
 4
 5        request.onupgradeneeded = function (event) {
 6            const db = event.target.result;
 7
 8            // Initialization process (creating object stores and indexes, etc.)
 9            const store = db.createObjectStore('users', { keyPath: 'id' });
10            store.createIndex('name', 'name', { unique: false });
11        };
12
13        request.onsuccess = function (event) {
14            const db = event.target.result;
15            resolve(db);
16        };
17
18        request.onerror = function () {
19            reject(request.error);
20        };
21    });
22}
23
24function addUserAsync(db, user) {
25    return new Promise((resolve, reject) => {
26        const transaction = db.transaction('users', 'readwrite');
27        const store = transaction.objectStore('users');
28        const request = store.add(user);
29
30        request.onsuccess = () => resolve();
31        request.onerror = () => reject(request.error);
32    });
33}
34
35async function main() {
36    try {
37        const db = await openDatabase();
38        await addUserAsync(db, { id: 1, name: 'Alice' });
39        console.log('User added successfully');
40    } catch (error) {
41        console.error('Error:', error);
42    }
43}
  • ห่อหุ้มฟังก์ชันเช่น openDatabase หรือ addUserAsync ด้วย Promise จะช่วยให้คุณสามารถจัดการกับกระบวนการแบบอัสินโครนัสได้อย่างเป็นธรรมชาติด้วย async/await
  • สิ่งนี้ช่วยหลีกเลี่ยงปัญหา callback hell และทำให้โค้ดอ่านง่ายขึ้น

สรุป

IndexedDB เป็นฟีเจอร์ที่มีประสิทธิภาพมากเมื่อคุณต้องการจัดการข้อมูลขั้นสูงที่ฝั่งเบราว์เซอร์ ในตอนแรก คุณอาจสับสนกับการประมวลผลแบบอัสินโครนัสที่อิงตามเหตุการณ์ แต่เมื่อเข้าใจโครงสร้างแล้ว คุณจะสามารถดำเนินการจัดการข้อมูลขนาดใหญ่ที่ฝั่งไคลเอนต์ได้

โดยเฉพาะอย่างยิ่ง หากคุณคำนึงถึงจุดต่อไปนี้ จะทำให้คุณใช้งานได้อย่างราบรื่นมากขึ้น:

  • ในการตั้งค่าเริ่มต้น ให้ใช้ onupgradeneeded
  • ให้ความสำคัญกับโหมดอ่าน/เขียนของธุรกรรม
  • การใช้ดัชนี (Index) ช่วยให้ค้นหาได้อย่างมีประสิทธิภาพ
  • ข้อมูลสามารถเก็บในรูปแบบอ็อบเจ็กต์ ซึ่งเข้ากันได้ดีกับ JSON

ด้วยการใช้งาน IndexedDB อย่างชำนาญ การบริหารจัดการข้อมูลสำหรับ PWA และแอปออฟไลน์จะง่ายขึ้นมาก

คุณสามารถติดตามบทความข้างต้นโดยใช้ Visual Studio Code บนช่อง YouTube ของเรา กรุณาตรวจสอบช่อง YouTube ด้วย

YouTube Video