这里为爱计算机编程的小学生整理的完整学习文章,在原代码里加入了大量比喻、图解思路(用文字描述)和分步讲解,让小朋友也能像读故事一样学懂 Promise。
【JavaScript】Promise 对象使用方式研究和理解
适合读者 :刚开始学 JavaScript、对异步操作感到好奇的小学生朋友们
学习目标:理解 Promise 是做什么的,能看懂代码,还能自己动手写一个简化版
文章目录
- [【JavaScript】Promise 对象使用方式研究和理解](#【JavaScript】Promise 对象使用方式研究和理解)
-
- [一、Promise 是什么?一个生活中的例子](#一、Promise 是什么?一个生活中的例子)
- [二、Promise 的基本用法](#二、Promise 的基本用法)
- 三、原代码的问题分析
-
- [问题 1:类里不能用 `var`](#问题 1:类里不能用
var) - [问题 2:`then` 没有返回新的 Promise](#问题 2:
then没有返回新的 Promise) - [问题 3:异步支持不够](#问题 3:异步支持不够)
- [问题 4:状态值用数字不够直观](#问题 4:状态值用数字不够直观)
- [问题 1:类里不能用 `var`](#问题 1:类里不能用
- [四、手把手实现 MyPromise](#四、手把手实现 MyPromise)
-
- 第一步:最简骨架(能跑起来)
- 第二步:支持异步操作(解决"妈妈还没回来"的问题)
- [第三步:支持链式调用(真正的 Promise 魔法)](#第三步:支持链式调用(真正的 Promise 魔法))
- 五、静态方法小课堂
-
- [1. `Promise.resolve()` ------ 立刻成功的 Promise](#1.
Promise.resolve()—— 立刻成功的 Promise) - [2. `Promise.reject()` ------ 立刻失败的 Promise](#2.
Promise.reject()—— 立刻失败的 Promise) - [3. `Promise.all()` ------ 等待所有任务完成](#3.
Promise.all()—— 等待所有任务完成)
- [1. `Promise.resolve()` ------ 立刻成功的 Promise](#1.
- [六、完整版 MyPromise 代码(可直接复制运行)](#六、完整版 MyPromise 代码(可直接复制运行))
- 七、给小朋友的记忆口诀
- 八、课后小挑战
一、Promise 是什么?一个生活中的例子
想象你让妈妈帮你买一个玩具:
- 妈妈说:"好的,我答应你,明天去买。"
- 这时候你不知道能不能买到,只能等待。
- 第二天有两种结果:
- 买到了(成功)→ 你很开心,拿着玩具去玩。
- 没买到(失败)→ 你有点难过,妈妈解释了原因。
在 JavaScript 里,Promise(承诺)就像妈妈说的那句"我答应你"。它代表一个现在还没完成,但未来会有结果的事情。
Promise 有三种状态,就像红绿灯:
| 状态 | 英文 | 意思 | 比喻 |
|---|---|---|---|
| 🟡 黄灯 | pending |
等待中 | 妈妈正在去买玩具的路上 |
| 🟢 绿灯 | fulfilled |
已成功 | 玩具买到啦! |
| 🔴 红灯 | rejected |
已失败 | 商店关门了,没买到 |
重要规则 :一旦从黄灯变成绿灯或红灯,就不能再变回去了!就像玩具已经买到了,不可能突然变成没买到。
二、Promise 的基本用法
在真正动手写之前,先看看真正的 Promise 是怎么用的:
javascript
// 创建一个新的 Promise
const myPromise = new Promise((resolve, reject) => {
// 这里放异步操作,比如定时器、网络请求
setTimeout(() => {
const success = true; // 假设任务成功了
if (success) {
resolve("玩具买到啦!"); // 🟢 绿灯:成功,把结果传出去
} else {
reject("商店关门了"); // 🔴 红灯:失败,把原因传出去
}
}, 1000);
});
// 使用 Promise
myPromise
.then((result) => {
console.log("成功:", result); // 收到 "玩具买到啦!"
})
.catch((error) => {
console.log("失败:", error); // 如果 reject,会进这里
})
.finally(() => {
console.log("不管成功失败,我都会执行!"); // 类似"最后打扫战场"
});
三个关键方法
| 方法 | 作用 | 什么时候执行 |
|---|---|---|
then() |
处理成功 | Promise 变成 fulfilled 时 |
catch() |
处理失败 | Promise 变成 rejected 时 |
finally() |
最后收尾 | 不管成功失败都会执行 |
三、原代码的问题分析
你之前写的 MyPromise 非常有想法!但里面有几个"小陷阱",我们先把它们找出来,这样才能进步:
问题 1:类里不能用 var
javascript
// ❌ 错误
class MyPromise {
var state = 0; // JavaScript 类里面不能这样写!
}
// ✅ 正确
class MyPromise {
constructor() {
this.state = 0; // 要用 this 来存放每个对象自己的数据
}
}
问题 2:then 没有返回新的 Promise
真正的 Promise 可以链式调用 (.then().then().catch()),因为每个 then 都会返回一个新的 Promise。原代码缺少这个。
问题 3:异步支持不够
如果 resolve 是在 setTimeout 里调用的,原代码可能收不到通知。
问题 4:状态值用数字不够直观
0, 1, 2, 3 容易搞混,我们用字符串 'pending', 'fulfilled', 'rejected' 更清晰。
四、手把手实现 MyPromise
我们分三步来实现,就像搭积木一样,从简单到完整。
第一步:最简骨架(能跑起来)
先做一个"能工作"的最小版本,理解核心逻辑:
javascript
class MyPromise {
constructor(executor) {
// 初始化状态和数据
this.state = 'pending'; // 一开始是黄灯:等待中
this.value = undefined; // 成功时带的数据
this.reason = undefined; // 失败时带的原因
// 定义 resolve 函数:任务成功时调用
const resolve = (value) => {
if (this.state === 'pending') { // 只有黄灯才能变绿灯
this.state = 'fulfilled';
this.value = value;
}
};
// 定义 reject 函数:任务失败时调用
const reject = (reason) => {
if (this.state === 'pending') { // 只有黄灯才能变红灯
this.state = 'rejected';
this.reason = reason;
}
};
// 立刻执行用户传入的函数
try {
executor(resolve, reject);
} catch (err) {
reject(err); // 如果执行时出错,直接变红灯
}
}
// 注册成功回调
then(onFulfilled) {
if (this.state === 'fulfilled') {
onFulfilled(this.value);
}
}
// 注册失败回调
catch(onRejected) {
if (this.state === 'rejected') {
onRejected(this.reason);
}
}
}
// ========== 测试一下 ==========
const p = new MyPromise((resolve, reject) => {
resolve("Hello, Promise!");
});
p.then((msg) => {
console.log(msg); // 输出:Hello, Promise!
});
小朋友思考题 :为什么 resolve 和 reject 里面都要判断 this.state === 'pending'?
答案 :防止有人不小心调用了两次 resolve,Promise 的状态只能改变一次!
第二步:支持异步操作(解决"妈妈还没回来"的问题)
上面的代码有个大问题:如果 resolve 是在 1 秒后才执行的,而 then 已经提前注册好了,就会错过通知。
就像妈妈还没买回玩具,你就一直问"买到了吗",结果妈妈回来时你已经去睡觉了,没听到消息。
解决办法:准备一个"留言本"(回调队列)。
javascript
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = []; // 成功留言本
this.onRejectedCallbacks = []; // 失败留言本
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
// 妈妈回来了!按留言本上的名单逐个通知
this.onFulfilledCallbacks.forEach(fn => fn(value));
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn(reason));
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
onFulfilled(this.value);
} else if (this.state === 'rejected') {
onRejected(this.reason);
} else {
// 还是黄灯状态?先把回调存到留言本里
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
}
catch(onRejected) {
this.then(null, onRejected);
}
}
// ========== 测试异步 ==========
const p2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("异步任务完成!");
}, 1000);
});
p2.then((msg) => {
console.log(msg); // 1秒后输出:异步任务完成!
});
形象理解:
pending时:then把函数写进"留言本",不立刻执行。fulfilled时:按留言本顺序,一个个打电话通知。
第三步:支持链式调用(真正的 Promise 魔法)
真正的 Promise 最酷的地方是可以链式调用:
javascript
fetchData()
.then(data => process(data))
.then(result => save(result))
.catch(err => console.error(err));
要实现链式调用,then 必须返回一个新的 Promise。
javascript
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
// 返回新的 Promise,才能继续 .then()
return new MyPromise((resolve, reject) => {
const handleFulfilled = () => {
try {
// 执行用户的回调,拿到返回值
const result = onFulfilled ? onFulfilled(this.value) : this.value;
resolve(result); // 把返回值传给下一个 Promise
} catch (err) {
reject(err); // 如果回调里报错,下一个 Promise 失败
}
};
const handleRejected = () => {
try {
const result = onRejected ? onRejected(this.reason) : this.reason;
resolve(result);
} catch (err) {
reject(err);
}
};
if (this.state === 'fulfilled') {
handleFulfilled();
} else if (this.state === 'rejected') {
handleRejected();
} else {
// pending 状态,先存起来
this.onFulfilledCallbacks.push(handleFulfilled);
this.onRejectedCallbacks.push(handleRejected);
}
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
finally(onFinally) {
return this.then(
(value) => {
onFinally(); // 成功时执行 finally
return value; // 把值继续传下去
},
(reason) => {
onFinally(); // 失败时执行 finally
throw reason; // 把错误继续抛下去
}
);
}
}
// ========== 链式调用测试 ==========
const p3 = new MyPromise((resolve, reject) => {
setTimeout(() => resolve(10), 500);
});
p3
.then(num => {
console.log("第一步:", num); // 10
return num * 2; // 返回 20
})
.then(num => {
console.log("第二步:", num); // 20
return num + 5; // 返回 25
})
.then(num => {
console.log("第三步:", num); // 25
})
.finally(() => {
console.log("全部完成!");
});
链式调用的秘密:
每个
.then()都是一座"小桥",桥的这头接收上一个 Promise 的结果,那头把新结果传给下一个 Promise。只要每座桥都返回新的MyPromise,就能一直连下去!
五、静态方法小课堂
真正的 Promise 还有一些"快捷方式",我们也来简单实现一下:
1. Promise.resolve() ------ 立刻成功的 Promise
javascript
MyPromise.resolve = function(value) {
return new MyPromise((resolve) => resolve(value));
};
// 用法
MyPromise.resolve("直接成功").then(msg => console.log(msg));
2. Promise.reject() ------ 立刻失败的 Promise
javascript
MyPromise.reject = function(reason) {
return new MyPromise((resolve, reject) => reject(reason));
};
3. Promise.all() ------ 等待所有任务完成
就像你和三个朋友比赛做作业,要等所有人都做完才能一起去玩。
javascript
MyPromise.all = function(promises) {
return new MyPromise((resolve, reject) => {
const results = [];
let count = 0;
promises.forEach((p, index) => {
p.then(value => {
results[index] = value; // 按顺序保存结果
count++;
if (count === promises.length) {
resolve(results); // 全部完成!
}
}).catch(err => reject(err)); // 有一个失败就全失败
});
});
};
// 测试
const pA = MyPromise.resolve("苹果");
const pB = new MyPromise(r => setTimeout(() => r("香蕉"), 100));
const pC = MyPromise.resolve("樱桃");
MyPromise.all([pA, pB, pC]).then(fruits => {
console.log(fruits); // ["苹果", "香蕉", "樱桃"]
});
六、完整版 MyPromise 代码(可直接复制运行)
把上面学的全部拼起来,就是一个相对完整的版本啦:
javascript
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
const handleFulfilled = () => {
try {
const result = onFulfilled ? onFulfilled(this.value) : this.value;
resolve(result);
} catch (err) {
reject(err);
}
};
const handleRejected = () => {
try {
const result = onRejected ? onRejected(this.reason) : this.reason;
resolve(result);
} catch (err) {
reject(err);
}
};
if (this.state === 'fulfilled') {
handleFulfilled();
} else if (this.state === 'rejected') {
handleRejected();
} else {
this.onFulfilledCallbacks.push(handleFulfilled);
this.onRejectedCallbacks.push(handleRejected);
}
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
finally(onFinally) {
return this.then(
value => { onFinally(); return value; },
reason => { onFinally(); throw reason; }
);
}
// 静态方法
static resolve(value) {
return new MyPromise(r => r(value));
}
static reject(reason) {
return new MyPromise((_, r) => r(reason));
}
static all(promises) {
return new MyPromise((resolve, reject) => {
const results = [];
let count = 0;
promises.forEach((p, i) => {
p.then(v => {
results[i] = v;
count++;
if (count === promises.length) resolve(results);
}).catch(reject);
});
});
}
}
七、给小朋友的记忆口诀
Promise 像承诺,三种状态要记清:
pending 黄灯等一等,fulfilled 绿灯行,
rejected 红灯停一停。
then 成功 catch 失败,finally 收尾不管成败。
链式调用像搭桥,返回新 Promise 才能跑。
异步不用怕,留言本把回调存下。
八、课后小挑战
- 改一改 :试着给
MyPromise添加一个MyPromise.race()方法,实现"多个 Promise 赛跑,谁第一个完成就用谁的结果"。 - 想一想 :如果
then的回调里返回的是另一个MyPromise,会发生什么?(提示:这叫"Promise 穿透") - 做一做 :用
MyPromise模拟一个"订外卖"的场景:下单 → 商家接单(1秒)→ 骑手配送(2秒)→ 送达,每一步都用.then()连接。
学习寄语:Promise 看起来有点绕,但其实就像生活中的"等一等,有结果了我告诉你"。多写几遍,多改几个例子,你就会发现它其实很讲道理。加油,小程序员!🚀
