自定义交易组件商详下单一体回调中如何等待request返回?
结论
setDetailOrderInfoChangeCallback 回调中可以发起 request。真正的问题是网络请求会异步执行:外层回调已经 return 后,请求的 success 回调或 Promise 才完成。因此,控制台会依次打印:
- 下单页面回调
- callbackData
- response
要让组件等待请求结果,外层回调必须返回 Promise,并在运费、商家优惠等请求全部完成后再 resolve。使用 async/await 时,需要先 await 请求结果,再 return 最终数据。
原因
下面的写法无法等待请求完成:
setDetailOrderInfoChangeCallback((callbackData) => {
let result
wx.request({
url: 'https://example.com/order/calculate',
data: callbackData,
success(res) {
console.log('response', res)
result = res.data
}
})
console.log('callbackData', callbackData)
return result
})
wx.request 发起请求后,程序会立即执行后面的代码。此时 success 还没有触发,result 仍是 undefined,外层回调却已经结束了。
在 success 中写 return res.data 同样无效,因为这个值是从 success 函数返回的,并不会成为 setDetailOrderInfoChangeCallback 外层回调的返回值。
推荐写法:将请求封装为 Promise
先将 wx.request 包装成 Promise:
function request(options) {
return new Promise((resolve, reject) => {
wx.request({
...options,
success(res) {
resolve(res)
},
fail(err) {
reject(err)
}
})
})
}
然后在回调中使用 async/await:
setDetailOrderInfoChangeCallback(async (callbackData) => {
console.log('下单页面回调')
console.log('callbackData', callbackData)
try {
const response = await request({
url: 'https://example.com/order/calculate',
method: 'POST',
data: callbackData
})
console.log('response', response)
if (response.statusCode < 200 || response.statusCode >= 300) {
throw new Error(`请求失败,statusCode: ${response.statusCode}`)
}
const orderInfo = response.data
return {
// 按组件文档要求组装返回值
// freight: orderInfo.freight,
// merchantDiscount: orderInfo.merchantDiscount
}
} catch (error) {
console.error('查询运费和商家优惠失败', error)
return {
// 按组件约定返回兜底数据或错误信息
}
}
})
async 函数会自动返回 Promise。代码执行到 await request(...) 时,会先等待请求完成,再继续组装并返回结果。
返回对象的字段必须与当前组件文档中的定义一致。由于问题没有提供组件版本和返回协议,这里不补充具体字段。
同时查询运费和优惠
如果运费和商家优惠来自两个相互独立的接口,可以用 Promise.all 并行请求:
setDetailOrderInfoChangeCallback(async (callbackData) => {
try {
const [freightResponse, discountResponse] = await Promise.all([
request({
url: 'https://example.com/order/freight',
method: 'POST',
data: callbackData
}),
request({
url: 'https://example.com/order/discount',
method: 'POST',
data: callbackData
})
])
return {
// 按组件要求转换并返回数据
// freight: freightResponse.data,
// merchantDiscount: discountResponse.data
}
} catch (error) {
console.error('订单信息计算失败', error)
return {
// 返回符合组件协议的兜底结果
}
}
})
如果优惠金额依赖运费结果,就需要串行请求:
setDetailOrderInfoChangeCallback(async (callbackData) => {
const freightResponse = await request({
url: 'https://example.com/order/freight',
method: 'POST',
data: callbackData
})
const discountResponse = await request({
url: 'https://example.com/order/discount',
method: 'POST',
data: {
...callbackData,
freight: freightResponse.data
}
})
return {
// 按组件协议组装最终返回值
}
})
不使用 async/await 的写法
也可以直接返回 Promise:
setDetailOrderInfoChangeCallback((callbackData) => {
return request({
url: 'https://example.com/order/calculate',
method: 'POST',
data: callbackData
}).then((response) => {
console.log('response', response)
return {
// 按组件协议组装返回值
}
}).catch((error) => {
console.error('查询失败', error)
return {
// 按组件协议返回兜底结果
}
})
})
是否使用 async/await 并不重要。需要保证的是,最外层回调必须将表示请求完成状态的 Promise 返回给组件。
注意事项
- 不要在请求发出后立即返回一个尚未赋值的变量。
- 不要只在
success回调中写return,这个返回值无法传递给外层回调。 - 确认项目中的
request封装确实返回了 Promise。有些请求函数虽然名称相同,内部却没有return new Promise(...)。 - 请求失败、超时或返回非成功状态码时,也要提供符合组件协议的返回值,避免下单页面一直等待。
- 商品数量、规格或地址发生变化时,这个回调可能被频繁触发。必要时可以使用请求序号或取消机制,防止先发出的请求后返回,进而覆盖最新的计算结果。
- 如果当前版本的组件明确规定回调只能同步返回,不接受 Promise,就无法在回调内等待网络请求,只能提前请求并缓存结果。组件是否支持 Promise,应以当前版本的官方接口定义为准。
备注:内容仅供参考。