new Promise(executor) สร้าง Promise ที่ยังไม่รู้ผล
executor รับ resolve และ reject — คุณเป็นคนตัดสินใจว่าจะเรียกตัวไหนเมื่อไหร่
Promises
สร้าง Promise ด้วย new Promise() — เข้าใจ executor, resolve, reject, จังหวะที่ executor ทำงาน และเมื่อไหร่ควรสร้าง Promise เอง
ในหน้าที่แล้ว (Promise Basics) คุณเรียน `Promise.resolve(value)` สำหรับสร้าง Promise ที่สำเร็จทันที — เหมาะเมื่อคุณมีค่าพร้อมอยู่แล้ว แต่ในงานจริง คุณมักต้องสร้าง Promise จากงานที่ **ยังไม่รู้ผลล่วงหน้า** — เช่น: - รอข้อมูลจาก API - ตรวจสอบสิทธิ์ก่อนโหลดข้อมูล - รอไฟล์อ่านเสร็จ **`new Promise(executor)`** ให้คุณเขียนโค้ดที่ตัดสินใจเองว่าจะ resolve (สำเร็จ) หรือ reject (ล้มเหลว) เมื่อไหร่
**Syntax**: ``` var promise = new Promise(function (resolve, reject) { // executor — โค้ดที่คุณเขียน // เรียก resolve(value) เมื่อสำเร็จ // เรียก reject(error) เมื่อล้มเหลว }); ``` **executor** คือ function ที่คุณเขียน — JavaScript จะส่ง function 2 ตัวให้: - **`resolve`** — function สำหรับบอกว่างานสำเร็จ → ส่งค่าไป `.then()` - **`reject`** — function สำหรับบอกว่างานล้มเหลว → ส่ง error ไป `.catch()`
executor คือ function ที่คุณเขียน
ภายใน executor คุณมี resolve กับ reject ให้เรียกตามเงื่อนไข — คุณเป็นคนตัดสินใจว่าเมื่อไหร่จะสำเร็จหรือล้มเหลว ไม่ใช่ JavaScript
สร้าง Promise ด้วย new Promise() — ยังไม่เรียก resolve หรือ reject → สถานะเป็น pending
// สร้าง Promise — ยังไม่เรียก resolve/reject
var pending = new Promise(function (resolve, reject) {
// ยังไม่ตัดสินใจ — Promise อยู่ในสถานะ pending
});
console.log(pending);
// Promise { <pending> }
// สร้าง Promise แล้ว resolve ทันที
var fulfilled = new Promise(function (resolve, reject) {
resolve("เสร็จแล้ว");
});
console.log(fulfilled);
// Promise { "เสร็จแล้ว" }สิ่งที่ต้องเข้าใจคือ **executor รันแบบ synchronous ทันที** ตอนที่ `new Promise()` ถูกเรียก — โค้ดใน executor รันเสร็จก่อนที่บรรทัดถัดไปจะทำงาน แต่ผลของ `resolve()` และ `reject()` ถูกส่งต่อแบบ **asynchronous** — callback ใน `.then()` จะทำงานทีหลังเสมอ เหมือนที่เรียนในหน้า Promise Basics
console.log ใน executor รันก่อน log ข้างนอก — แต่ .then() callback รันทีหลัง
var promise = new Promise(function (resolve, reject) {
console.log("ข้อ 1: executor รันทันที"); // sync
resolve("ข้อมูล");
console.log("ข้อ 2: หลัง resolve ใน executor"); // sync
});
console.log("ข้อ 3: หลัง new Promise()"); // sync
promise.then(function (value) {
console.log("ข้อ 4:", value); // async — รันทีหลังสุด
});
// output:
// ข้อ 1: executor รันทันที
// ข้อ 2: หลัง resolve ใน executor
// ข้อ 3: หลัง new Promise()
// ข้อ 4: ข้อมูลลำดับ output: **1 → 2 → 3 → 4** - **ข้อ 1, 2**: executor รัน sync ทันที — `resolve()` ถูกเรียกแต่ `.then()` callback ยังไม่ทำงาน - **ข้อ 3**: โค้ดข้างนอก `new Promise()` รันต่อ - **ข้อ 4**: `.then()` callback รันทีหลัง — เพราะ resolve ส่งผลแบบ async
executor รัน sync — resolve ส่งผล async
โค้ดใน executor ทำงานเหมือนโค้ดปกติ (sync) แต่การที่ค่าจาก resolve ไปถึง .then() เป็น async — เหมือน Promise.resolve() ที่เรียนไป
เรียก `resolve(value)` เพื่อเปลี่ยน Promise จาก `pending` → `fulfilled` — ค่า `value` จะถูกส่งไปยัง callback ใน `.then()` รูปแบบที่ใช้บ่อยที่สุดคือ **function ที่ return Promise** — เพื่อให้ผู้เรียกใช้สามารถต่อ `.then()` ได้:
loadConfig return Promise — resolve ส่ง config object ไป .then()
function loadConfig() {
return new Promise(function (resolve, reject) {
// สมมติว่าโหลด config จากที่ไหนสักแห่ง
var config = { theme: "dark", lang: "th" };
resolve(config); // สำเร็จ → ส่ง config ไป .then()
});
}
// ใช้งาน — ต่อ .then() ได้เลย
loadConfig().then(function (config) {
console.log("theme:", config.theme);
console.log("lang:", config.lang);
});
// output:
// theme: dark
// lang: thgetPrice return Promise — resolve ราคาตามชื่อสินค้าที่ส่งเข้ามา
function getPrice(item) {
return new Promise(function (resolve, reject) {
// สมมติว่าดึงราคาจากฐานข้อมูล
var prices = {
"กาแฟ": 55,
"ชา": 45,
"โกโก้": 60,
};
resolve(prices[item]); // ส่งราคาไป .then()
});
}
getPrice("กาแฟ").then(function (price) {
console.log("ราคา:", price);
});
// output:
// ราคา: 55สังเกต: function ที่ return Promise สามารถต่อ `.then()` ได้เลย — เหมือนกับ `Promise.resolve()` ที่เรียนไป ต่างกันตรงที่ `Promise.resolve()` resolve ทันทีด้วยค่าที่กำหนด — แต่ `new Promise()` ให้คุณเขียน logic ใน executor ก่อนจะตัดสินใจว่าจะ resolve อะไร
เรียก `reject(error)` เพื่อเปลี่ยน Promise จาก `pending` → `rejected` — error จะถูกส่งไปยัง `.catch()` แทน `.then()` ในงานจริง คุณมักใช้ resolve และ reject ด้วยกัน — ตัดสินใจจากเงื่อนไข:
เราจะเรียน .catch() อย่างละเอียดในหน้าถัดไป
ตอนนี้รู้แค่ว่า reject ส่ง error ไปให้ .catch() พอ — หน้า catch() & finally() จะสอน .catch() ทั้ง recovery, fallback value และ unhandled rejection
loadUser รับ isLoggedIn — true → resolve ข้อมูล, false → reject error
function loadUser(isLoggedIn) {
return new Promise(function (resolve, reject) {
if (isLoggedIn) {
resolve({ name: "Mali", role: "member" });
} else {
reject(new Error("กรุณาเข้าสู่ระบบก่อน"));
}
});
}
// เรียกใช้ — สำเร็จ
loadUser(true)
.then(function (user) {
console.log("ยินดีต้อนรับ", user.name);
})
.catch(function (err) {
console.log("ไม่สำเร็จ:", err.message);
});
// output:
// ยินดีต้อนรับ Mali
// เรียกใช้ — ล้มเหลว
loadUser(false)
.then(function (user) {
console.log("ยินดีต้อนรับ", user.name);
})
.catch(function (err) {
console.log("ไม่สำเร็จ:", err.message);
});
// output:
// ไม่สำเร็จ: กรุณาเข้าสู่ระบบก่อนสังเกต: - `loadUser(true)` → `resolve({ name: "Mali" })` → `.then()` ได้รับ user object - `loadUser(false)` → `reject(new Error("..."))` → `.catch()` ได้รับ error - ทั้งสองกรณีใช้ function เดียวกัน — เปลี่ยนแค่เงื่อนไข **ใช้ `reject(new Error("..."))` ไม่ใช่ `reject("string")`** — ในบท Error (บทที่ 104) คุณเรียนว่า Error object มี `.message` และ `.stack` ช่วย debug — ดังนั้น reject ควรส่ง Error object เสมอ
resolve ส่งค่าไป .then() — reject ส่ง error ไป .catch() — สถานะต่างกัน จุดหมายต่างกัน
// resolve ส่งค่าไป .then()
new Promise(function (resolve, reject) {
resolve("สำเร็จ");
})
.then(function (value) {
console.log("then:", value); // then: สำเร็จ
})
.catch(function (err) {
console.log("catch:", err.message); // ไม่ทำงาน
});
// reject ส่ง error ไป .catch()
new Promise(function (resolve, reject) {
reject(new Error("ล้มเหลว"));
})
.then(function (value) {
console.log("then:", value); // ไม่ทำงาน
})
.catch(function (err) {
console.log("catch:", err.message); // catch: ล้มเหลว
});ถึงตอนนี้คุณรู้จัก 2 วิธีสร้าง Promise: - **`Promise.resolve(value)`** — สร้าง Promise ที่ fulfilled ทันที - **`new Promise(executor)`** — สร้าง Promise ที่คุณควบคุม resolve/reject เอง การเลือกใช้ขึ้นอยู่กับว่าคุณรู้ผลลัพธ์แล้วหรือยัง:
แทนที่จะส่ง callback ให้ setTimeout — ห่อด้วย new Promise แล้ว return Promise ออกมา
// ก่อน: callback-based (บท Timer)
// setTimeout(function () {
// console.log("ผ่านไป 1 วินาที");
// }, 1000);
// หลัง: return Promise แทน callback
function delay(ms) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("ผ่านไป " + ms + " มิลลิวินาที");
}, ms);
});
}
// ใช้งาน — ต่อ .then() ได้เลย ไม่ต้องส่ง callback
delay(1000).then(function (message) {
console.log(message);
});
// output (หลัง 1 วินาที):
// ผ่านไป 1000 มิลลิวินาทีตัวอย่าง `delay()` คือ **แปลง callback-based API เป็น Promise** — เทคนิคนี้ใช้บ่อยมากในการทำให้โค้ด async อ่านง่ายขึ้น: - **ก่อน**: ส่ง callback ให้ `setTimeout` — callback ซ้อนกันเมื่อมีหลายขั้นตอน - **หลัง**: `delay()` return Promise — ต่อ `.then()` ได้เลย อ่านจากบนลงล่าง **สรุป: เมื่อไหร่ใช้อะไร**:
executor รับ resolve และ reject — คุณเป็นคนตัดสินใจว่าจะเรียกตัวไหนเมื่อไหร่
ค่า value ถูกส่งไป .then() — เหมือน Promise.resolve(value) แต่คุณควบคุมจังหวะเอง
error ถูกส่งไป .catch() — ใช้ reject(new Error(...)) เพื่อให้มี .message และ .stack
โค้ดใน executor ทำงานแบบ synchronous แต่ .then() callback ทำงานทีหลังเสมอ
Error object มี .message และ .stack ช่วย debug — เชื่อมจากบท Error
ห่อ callback ไว้ใน executor — เรียก resolve ใน callback เมื่อสำเร็จ