JS类异步数据处理:解决fetch请求后访问数据为空问题
问题核心原因
你遇到的是JavaScript异步执行机制的典型问题:fetch发起网络请求属于异步宏任务,构造函数中调用fetchNews()时,主线程不会等待接口响应返回、也不会等待Promise状态变更为resolved,就会直接往下执行后续的showNews()调用,此时类成员news还没被接口返回值赋值,自然读取到的是初始的空数组。
从你代码里的打印顺序就能验证:控制台永远会先打印B、C的空数组结果,最后才打印A的接口数据长度,就是异步任务被延后到同步任务执行完成后才触发的表现。
另外你原代码构造函数里单独写的this.news;是无效语句,没有任何实际作用,可以直接删掉。
可落地的调整方案
不要试图在构造函数里阻塞等待异步结果,构造函数本身不支持返回Promise来阻塞实例化流程,强行实现会让代码可维护性极差。推荐两种常用的实现方式:
方案1:最小改动,返回Promise实例控制执行顺序
只需要让fetchNews()方法返回内部的请求Promise,外部实例化后等待请求完成,再调用渲染方法即可,改动量最小:
class MyNews { news = []; apiUrl = "https://jsonplaceholder.typicode.com/posts"; constructor() { // 移除原无效的this.news语句,构造函数里只做同步初始化 } fetchNews() { // 返回整个请求的Promise链 return fetch(this.apiUrl) .then(async (res) => { this.news = await res.json(); console.log("A:", this.news.length); return this.news; }) .catch((err) => { console.log("请求异常:", err); throw err; }); } showNews() { console.log("B:", this.news.length); // 这里直接写DOM渲染逻辑即可,比如遍历this.news生成li标签插入页面列表容器 return this.news; } } // 实例化+调用逻辑 const newsInstance = new MyNews(); // 等待数据拉取完成后再执行渲染 newsInstance.fetchNews().then(() => { console.log("C:", newsInstance.showNews().length); // 所有依赖新闻数据的HTML渲染逻辑都放在这个回调里执行 });
方案2:async/await语法改造,逻辑更贴近同步写法
如果不习惯链式then的写法,可以用async/await改造,代码逻辑更直观:
class MyNews { news = []; apiUrl = "https://jsonplaceholder.typicode.com/posts"; // 标记方法为async async fetchNews() { try { const res = await fetch(this.apiUrl); this.news = await res.json(); console.log("A:", this.news.length); return this.news; } catch (err) { console.log("请求异常:", err); throw err; } } showNews() { // 加个防御性判断,避免未加载完成时调用报错 if (!this.news.length) { console.warn("新闻数据尚未加载完成"); return []; } console.log("B:", this.news.length); // DOM渲染逻辑写在这里 return this.news; } } // 用自执行async函数包裹初始化逻辑 (async () => { const newsInstance = new MyNews(); await newsInstance.fetchNews(); const newsList = newsInstance.showNews(); console.log("C:", newsList.length); // 执行后续列表渲染操作 })();
额外优化建议
- 可以在调用
fetchNews()之前先给页面渲染“数据加载中”的占位提示,等接口返回、showNews()执行时再替换成真实内容,提升用户体验。 - 不要把异步请求的结果赋值完全和渲染逻辑割裂,后续如果要做刷新、分页等功能,只需要重新调用
fetchNews(),数据更新完成后再触发showNews()重渲染即可。
内容的提问来源于stack exchange,提问作者Max Pattern
相关产品推荐
相关产品推荐

