本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
鸿蒙应用开发中,虽然JavaScript是单线程运行的,但可以通过 事件循环机制 和 异步I/O 实现"伪并发"的多个HTTP请求。
一、单线程并发请求的核心原理
1. 事件循环机制
2. 关键点说明
- 非阻塞I/O:实际网络请求由底层系统线程处理
- Promise微任务:
.then()
回调进入微任务队列 - 并发本质:请求发送是并行的,回调处理是串行的
二、实现单线程"并发"的3种方式
方法1:Promise.all + fetch(推荐)
import http from '@ohos.net.http';
async function concurrentRequests(urls: string[]) {
// 1. 创建多个未解决的Promise
const requests = urls.map(url => {
return new Promise((resolve, reject) => {
const httpRequest = http.createHttp();
httpRequest.request(
url,
{
method: 'GET',
connectTimeout: 5000
},
(err, data) => {
httpRequest.destroy();
err ? reject(err) : resolve(data.result);
}
);
});
});
// 2. 等待所有请求完成
try {
const results = await Promise.all(requests);
results.forEach((res, index) => {
console.log(`请求${urls[index]}结果:`, res);
});
} catch (e) {
console.error('某个请求失败:', e);
}
}
// 使用示例
const apiUrls = [
'https://api.example.com/data1',
'https://api.example.com/data2',
'https://api.example.com/data3'
];
concurrentRequests(apiUrls);
方法2:for...of + await(可控并发)
async function controlledConcurrent(urls: string[], maxConcurrent = 3) {
const results = [];
const executing = new Set();
for (const url of urls) {
// 1. 创建Promise但不立即await
const promise = new Promise((resolve, reject) => {
const httpRequest = http.createHttp();
httpRequest.request(url, (err, data) => {
httpRequest.destroy();
err ? reject(err) : resolve(data.result);
});
});
// 2. 记录执行中的Promise
const task = promise.finally(() => {
executing.delete(task);
});
executing.add(task);
// 3. 控制并发数
if (executing.size >= maxConcurrent) {
await Promise.race(executing);
}
}
// 4. 等待剩余任务
return Promise.all([...executing]);
}
方法3:Promise.allSettled(获取全部结果)
async function allSettledRequests(urls: string[]) {
const requests = urls.map(url => {
return new Promise((resolve) => {
const httpRequest = http.createHttp();
httpRequest.request(url, (err, data) => {
httpRequest.destroy();
resolve({
url,
status: err ? 'rejected' : 'fulfilled',
data: err || data.result
});
});
});
});
return await Promise.allSettled(requests);
}
三、底层原理深度解析
1. libuv事件循环
鸿蒙使用的JS引擎通过libuv实现:
- 网络I/O:由系统线程池处理(Windows使用IOCP,Linux用epoll)
- 回调队列:分为宏任务队列和微任务队列
- 典型执行顺序:
同步代码 → 微任务队列 → 宏任务队列 → 渲染更新
2. HTTP请求生命周期
3. 性能瓶颈点
环节 | 优化方向 |
---|---|
DNS解析 | 使用DNS缓存 |
TCP连接建立 | 保持长连接 |
线程池竞争 | 控制并发数 |
JSON解析 | 流式解析器 |
回调处理 | 减少主线程阻塞操作 |
四、与多线程方案的对比
特性 | 单线程Promise | TaskPool多线程 |
---|---|---|
CPU利用率 | 低(主线程串行) | 高(多核并行) |
内存开销 | 小 | 较大(每个线程独立) |
适用场景 | IO密集型 | CPU密集型 |
开发复杂度 | 简单 | 中等 |
典型延迟 | 1-100ms | 10-500ms |
建议
- IO密集型任务:优先使用单线程Promise并发
- 超过50个请求:考虑分片处理(每片5-10个)
- 关键路径请求:使用
Promise.race()
实现快速失败