Next.js问题:如何确保router.push在所有API请求完成后触发
问题场景与需求
我有两个页面:
- 页面1:包含待审批文档,设有「审批」按钮。点击按钮的
onClick函数会通过Axios调用API,生成n个后续文档,这些文档创建后需要在页面2展示。 - 页面2:使用
getServerSideProps从数据库获取上述后续文档并渲染。
目前遇到的问题:页面1的审批按钮onClick函数中,router.push()会在所有API调用完成前触发,导致页面2的getServerSideProps无法获取到新生成的文档(必须手动硬刷新页面2才能看到)。
以下是当前的handleApproveClick函数代码:
const handleApproveClick = async () => { try { setLoading(true); const { data } = await axios.put( "https://blahblahblah.api.com/updateDocumentStatus/?docKey=sr&docType=stockrequests&status=Approved&docId=" + srId ); const uniqueSuppliers = [ ...new Set( stockRequest.sr_line_items.map( (supplier) => supplier.sr_line_item_supplier ) ), ]; console.log(uniqueSuppliers); const stockOrders = []; const createStockOrders = uniqueSuppliers .map(async (uniqueSupplier) => { const srFilterLineItems = await stockRequest.sr_line_items.filter( function (e) { return e.sr_line_item_supplier === uniqueSupplier; } ); const soLineItems = await srFilterLineItems.map( (srFilterLineItem, index) => ({ so_line_item_line_num: index + 1, so_line_item_sku_id: srFilterLineItem.sr_line_item_sku_id, so_line_item_description: srFilterLineItem.sr_line_item_description, so_line_item_uom: srFilterLineItem.sr_line_item_uom, so_line_item_uom_description: srFilterLineItem.sr_line_item_uom_description, so_line_item_price: srFilterLineItem.sr_line_item_price, so_line_item_quantity: srFilterLineItem.sr_line_item_quantity, so_line_item_total: srFilterLineItem.sr_line_item_total, }) ); console.log(soLineItems); const soCounter = await axios.put( "https://blahblahblah.api.com/counter/?counterName=so&venueId=RUL001" ); setSending(true); const { data } = await axios.post( "https://blahblahblah.api.com/postDocument/?docType=stockorders", { so_id: `SO-RUL001-${soCounter.data.value.so_number}`, so_id_num: `${soCounter.data.value.so_number}`, so_sr_id: srId, so_total: round( soLineItems.reduce((a, c) => a + c.so_line_item_total, 0) ), so_date_created: new Date(), createdAt: new Date(), so_delivery_date: new Date(), so_venue: "RUL001", so_supplier: uniqueSupplier, so_supplier_id: "Update1", so_status: "Created", so_payment_terms: "30 days", so_line_items: soLineItems, }, { headers: { "Access-Control-Allow-Origin": "http://localhost:3000/", "Content-Type": "application/json", }, } ); console.log(data); await axios.post("/api/emails/newstockordertosupplier", { ...data[0], }); }) router.push(`/my-approvals/stock-request/${srId}/stock-orders`); } catch (error) { console.log(error); } };
请问如何让router.push()仅在handleApproveClick中的所有API调用都完成后再触发?
解决方案
核心问题:uniqueSuppliers.map(async ...)会返回一个Promise数组,但你没有等待所有Promise执行完成就直接调用了router.push。同时代码里对同步的filter和map加了多余的await,可以去掉。
修改步骤:
- 用
Promise.all()等待所有供应商对应的API调用全部完成 - 移除
filter和map前多余的await(这两个是同步操作,不需要异步等待) - 将
router.push放在await Promise.all(createStockOrders)之后
修改后的代码:
const handleApproveClick = async () => { try { setLoading(true); const { data } = await axios.put( "https://blahblahblah.api.com/updateDocumentStatus/?docKey=sr&docType=stockrequests&status=Approved&docId=" + srId ); const uniqueSuppliers = [ ...new Set( stockRequest.sr_line_items.map( (supplier) => supplier.sr_line_item_supplier ) ), ]; console.log(uniqueSuppliers); const stockOrders = []; // 生成每个供应商对应的异步操作Promise数组 const createStockOrders = uniqueSuppliers .map(async (uniqueSupplier) => { // filter是同步操作,不需要await const srFilterLineItems = stockRequest.sr_line_items.filter( (e) => e.sr_line_item_supplier === uniqueSupplier ); // map是同步操作,不需要await const soLineItems = srFilterLineItems.map( (srFilterLineItem, index) => ({ so_line_item_line_num: index + 1, so_line_item_sku_id: srFilterLineItem.sr_line_item_sku_id, so_line_item_description: srFilterLineItem.sr_line_item_description, so_line_item_uom: srFilterLineItem.sr_line_item_uom, so_line_item_uom_description: srFilterLineItem.sr_line_item_uom_description, so_line_item_price: srFilterLineItem.sr_line_item_price, so_line_item_quantity: srFilterLineItem.sr_line_item_quantity, so_line_item_total: srFilterLineItem.sr_line_item_total, }) ); console.log(soLineItems); const soCounter = await axios.put( "https://blahblahblah.api.com/counter/?counterName=so&venueId=RUL001" ); setSending(true); const { data } = await axios.post( "https://blahblahblah.api.com/postDocument/?docType=stockorders", { so_id: `SO-RUL001-${soCounter.data.value.so_number}`, so_id_num: `${soCounter.data.value.so_number}`, so_sr_id: srId, so_total: round( soLineItems.reduce((a, c) => a + c.so_line_item_total, 0) ), so_date_created: new Date(), createdAt: new Date(), so_delivery_date: new Date(), so_venue: "RUL001", so_supplier: uniqueSupplier, so_supplier_id: "Update1", so_status: "Created", so_payment_terms: "30 days", so_line_items: soLineItems, }, { headers: { "Access-Control-Allow-Origin": "http://localhost:3000/", "Content-Type": "application/json", }, } ); console.log(data); await axios.post("/api/emails/newstockordertosupplier", { ...data[0], }); }); // 等待所有异步操作完成后再跳转 await Promise.all(createStockOrders); router.push(`/my-approvals/stock-request/${srId}/stock-orders`); } catch (error) { console.log(error); } finally { // 重置loading状态,避免异常时页面一直处于加载状态 setLoading(false); setSending(false); } };
补充说明:
Promise.all()会等待数组中所有Promise都resolve后才继续执行后续代码,确保所有文档生成API都完成,数据库中已有数据后再跳转到页面2。- 移除多余的
await可以避免不必要的异步等待,提升代码执行效率。 - 在
finally块中重置loading/sending状态,防止操作异常时页面一直处于加载状态。
内容的提问来源于stack exchange,提问作者Rich
相关产品推荐
相关产品推荐

