使用Axios嵌套API调用返回Promise对象而非实际数据求助
图书目录系统异步请求问题排查
问题背景
我正在开发一个包含四个目录的图书目录系统:
- All Directory
- Ongoing Directory
- Favorite Directory
- Completed Directory
需求是:图书对应的按钮,当图书处于Ongoing Directory时显示"Ongoing",未处于该目录时显示"Start Reading"。目前在获取全部图书的API调用内,对每本图书发起查询其是否在Ongoing Directory的API调用,但嵌套调用返回的是[promise object]而非实际数据,导致页面显示异常。
原代码
const books_div = document.querySelector('.root'); const showAllBooks = async () => { console.log('running'); try { const { data: { books } } = await axios.get('api/v1/home/all'); console.log(books); if (books.length < 1) { books_div.innerHTML = '<h5 class="empty-list">No Books in your Directory</h5>' return } const allBooks = books.map(async (book) => { const { name, authorName, _id: bookID } = book; var readingStatus = 'Start Reading'; try { const { data: { books: ongoingBooks } } = await axios.get(`api/v1/home/ongoing/${name}`); if (ongoingBooks.length !== 0) { readingStatus = 'Ongoing'; } } catch (error) { console.log(error); } return ( `<div class="single-book"> <div class="name-author-div"> <h4>${name}</h4> <p class="author-name">${authorName}</p> </div> <div class="icon-div"> <a class="favourite-btn" data-id="${bookID}"><img src="./icons/fav.svg" alt="star-icon"></a> <a class="delete-btn" data-id="${bookID}"><img src="./icons/dlt.svg" alt="bin-icon"></a> </div> <button class="btn">${readingStatus}</button> </div>` ); }).join(''); books_div.innerHTML = allBooks; console.log(`here is the book div ${books_div}`); } catch (error) { console.log(error); } } showAllBooks();
问题原因
books.map(async (book) => { ... })中,async函数的返回值是Promise对象,所以map遍历后得到的是一个Promise数组,而非HTML字符串数组。直接调用.join('')会把每个Promise对象转为字符串[object Promise],最终页面渲染的就是这些无效字符串。
解决方案
方案1:等待所有异步请求完成
使用Promise.all()等待map中所有Promise执行完毕,得到实际的HTML字符串数组后再拼接:
const books_div = document.querySelector('.root'); const showAllBooks = async () => { console.log('running'); try { const { data: { books } } = await axios.get('api/v1/home/all'); console.log(books); if (books.length < 1) { books_div.innerHTML = '<h5 class="empty-list">No Books in your Directory</h5>' return } // 生成每个图书对应的异步请求Promise const bookHtmlPromises = books.map(async (book) => { const { name, authorName, _id: bookID } = book; let readingStatus = 'Start Reading'; try { const { data: { books: ongoingBooks } } = await axios.get(`api/v1/home/ongoing/${name}`); if (ongoingBooks.length !== 0) { readingStatus = 'Ongoing'; } } catch (error) { console.log(error); } return ` <div class="single-book"> <div class="name-author-div"> <h4>${name}</h4> <p class="author-name">${authorName}</p> </div> <div class="icon-div"> <a class="favourite-btn" data-id="${bookID}"><img src="./icons/fav.svg" alt="star-icon"></a> <a class="delete-btn" data-id="${bookID}"><img src="./icons/dlt.svg" alt="bin-icon"></a> </div> <button class="btn">${readingStatus}</button> </div> `; }); // 等待所有Promise完成,获取HTML字符串数组 const allBooksHtml = await Promise.all(bookHtmlPromises); books_div.innerHTML = allBooksHtml.join(''); console.log(`here is the book div ${books_div}`); } catch (error) { console.log(error); } } showAllBooks();
方案2:优化API请求次数(推荐)
当前每本图书发起一次查询请求,属于N+1请求问题,性能较差。如果后端支持,建议先获取所有Ongoing目录的图书列表,再在前端匹配,只需两次API请求:
const books_div = document.querySelector('.root'); const showAllBooks = async () => { console.log('running'); try { // 并行请求全部图书和Ongoing目录图书,提升效率 const [allBooksResp, ongoingBooksResp] = await Promise.all([ axios.get('api/v1/home/all'), axios.get('api/v1/home/ongoing') // 假设该接口返回所有Ongoing状态的图书 ]); const { data: { books } } = allBooksResp; const { data: { books: ongoingBooks } } = ongoingBooksResp; // 将Ongoing图书的名称存入Set,快速判断图书状态 const ongoingBookNames = new Set(ongoingBooks.map(item => item.name)); if (books.length < 1) { books_div.innerHTML = '<h5 class="empty-list">No Books in your Directory</h5>' return } // 直接前端匹配状态,无需嵌套异步请求 const allBooksHtml = books.map(book => { const { name, authorName, _id: bookID } = book; const readingStatus = ongoingBookNames.has(name) ? 'Ongoing' : 'Start Reading'; return ` <div class="single-book"> <div class="name-author-div"> <h4>${name}</h4> <p class="author-name">${authorName}</p> </div> <div class="icon-div"> <a class="favourite-btn" data-id="${bookID}"><img src="./icons/fav.svg" alt="star-icon"></a> <a class="delete-btn" data-id="${bookID}"><img src="./icons/dlt.svg" alt="bin-icon"></a> </div> <button class="btn">${readingStatus}</button> </div> `; }).join(''); books_div.innerHTML = allBooksHtml; console.log(`here is the book div ${books_div}`); } catch (error) { console.log(error); } } showAllBooks();
内容的提问来源于stack exchange,提问作者Prince Kumar
相关产品推荐
相关产品推荐

