本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
一、Promise 就像「外卖订单」
想象你点了一份外卖:
-
下单时刻(Pending)
- 你付完钱,订单状态变成「制作中」
- 这时候你既没拿到饭,也没失败
-
结果到来
- 成功(Fulfilled):外卖员把饭送到你手里
- 失败(Rejected):餐厅打电话说「食材没了,退钱吧」
-
你的应对
- 成功时:打开包装开始吃(
.then()
) - 失败时:换一家店重新点(
.catch()
)
- 成功时:打开包装开始吃(
// 用代码表示就是:
const 外卖订单 = new Promise((成功, 失败) => {
if (餐厅有食材) {
成功("宫保鸡丁饭");
} else {
失败("食材缺货");
}
});
外卖订单
.then(饭 => console.log("开吃:" + 饭))
.catch(原因 => console.log("换一家,因为:" + 原因));
二、鸿蒙中为什么必须用 Promise?
1. 所有耗时操作都是异步的
以下操作必须通过 Promise 处理:
- 读取文件
- 网络请求
- 数据库查询
- 调用硬件(如摄像头)
// 鸿蒙文件读取示例
import fs from '@ohos.file.fs';
// 没有Promise的写法(错误!)
const file = fs.readFileSync('/data/test.txt'); // ❌ 同步读取会卡死界面
// 正确写法 ✅
fs.readFile('/data/test.txt').then(file => {
console.log("文件内容:", file);
});
2. 避免「回调地狱」
对比两种写法:
// 回调地狱写法(难以维护)
请求1(参数, () => {
请求2(参数, () => {
请求3(参数, () => {
// 更多嵌套...
});
});
});
// Promise链式调用(清晰)
请求1(参数)
.then(结果 => 请求2(参数))
.then(结果 => 请求3(参数))
.then(结果 => console.log("完成!"));
三、自己实现Promise的极简版
用一个简单的setTimeout模拟鸿蒙的异步操作:
class 我的Promise {
private 状态 = '等待'; // '等待' | '成功' | '失败'
private 结果: any;
private 回调队列: Function[] = [];
constructor(执行函数: (成功, 失败) => void) {
const 成功 = (值) => {
if (this.状态 !== '等待') return;
this.状态 = '成功';
this.结果 = 值;
this.执行回调();
};
const 失败 = (原因) => {
if (this.状态 !== '等待') return;
this.状态 = '失败';
this.结果 = 原因;
this.执行回调();
};
try {
执行函数(成功, 失败);
} catch (错误) {
失败(错误);
}
}
then(成功回调?: Function, 失败回调?: Function) {
return new 我的Promise((resolve, reject) => {
this.回调队列.push(() => {
try {
if (this.状态 === '成功') {
const 新结果 = 成功回调?.(this.结果) ?? this.结果;
resolve(新结果);
} else {
const 新结果 = 失败回调?.(this.结果) ?? this.结果;
reject(新结果);
}
} catch (错误) {
reject(错误);
}
});
if (this.状态 !== '等待') this.执行回调();
});
}
private 执行回调() {
// 模拟鸿蒙的异步调度
setTimeout(() => {
this.回调队列.forEach(回调 => 回调());
this.回调队列 = [];
}, 0);
}
}
// 使用示例
const 示例任务 = new 我的Promise((成功) => {
setTimeout(() => 成功("异步操作完成"), 1000);
});
示例任务.then(结果 => console.log(结果)); // 1秒后输出
四、开发中的实际应用
场景:同时加载用户数据和配置
import http from '@ohos.net.http';
import preferences from '@ohos.data.preferences';
// 1. 封装网络请求为Promise
function 获取用户信息(userId: string): Promise<any> {
return new Promise((resolve, reject) => {
const request = http.createHttp();
request.request(
`https://api.example.com/users/${userId}`,
{
method: 'GET',
connectTimeout: 60000,
},
(err, data) => err ? reject(err) : resolve(JSON.parse(data.result))
);
});
}
// 2. 封装本地存储为Promise
function 读取配置(): Promise<string> {
return new Promise((resolve, reject) => {
preferences.getPreferences('config', (err, prefs) => {
if (err) return reject(err);
prefs.get('theme', 'default', (err, value) => {
err ? reject(err) : resolve(value);
});
});
});
}
// 3. 合并多个异步操作
Promise.all([获取用户信息('123'), 读取配置()])
.then(([用户数据, 主题配置]) => {
console.log(`欢迎${用户数据.name},当前主题:${主题配置}`);
})
.catch(错误 => {
console.error('初始化失败:', 错误);
});
五、常见问题
1: 为什么不能直接用回调函数?
A: 当多个异步操作需要顺序执行时,回调会导致「金字塔缩进」:
获取用户(id, 用户 => {
获取订单(用户.id, 订单 => {
获取商品(订单.id, 商品 => {
// 更多嵌套...
});
});
});
而Promise可以通过链式调用保持代码扁平:
获取用户(id)
.then(用户 => 获取订单(用户.id))
.then(订单 => 获取商品(订单.id))
.then(商品 => console.log(商品));
2: async/await 和 Promise 什么关系?
A: async/await 是Promise的语法糖:
// 这两种写法完全等价
async function 示例1() {
const data = await 获取数据();
console.log(data);
}
function 示例2() {
获取数据().then(data => console.log(data));
}
3: 为什么鸿蒙的API都返回Promise?
A: 因为JavaScript是单线程的,如果同步执行耗时操作(如读取大文件),界面会完全卡住。Promise保证了所有耗时操作都不会阻塞主线程。
六、总结
-
Promise是什么 一个表示「未来才会完成的操作」的容器,有三种状态: ⏳ Pending(进行中) → ✅ Fulfilled(成功) / ❌ Rejected(失败)
-
鸿蒙中怎么用
- 所有耗时API(文件、网络、数据库)都返回Promise
- 用
.then()
处理成功,.catch()
处理失败
-
为什么需要它
- 避免界面卡顿(异步非阻塞)
- 解决回调地狱(链式调用)
- 统一异步操作标准(所有鸿蒙API都遵循)