Jest与Sinon.JS中server.requests的Promise特性疑问
这个问题的核心是两个点:Promise.all的灵活处理逻辑,以及你对Sinon fakeServer的requests数组的误解。咱们慢慢说清楚:
1. Promise.all根本不要求元素必须是Promise
Promise.all的设计非常灵活,它接收的数组里的元素可以是任何值——不一定非得是Promise对象。对于每个元素,它会自动用Promise.resolve(item)做一层包装:
- 如果item本身是Promise,就老老实实等它resolve/reject;
- 如果item是thenable对象(也就是有
.then()方法的普通对象),就会按照thenable的规则处理,等待它的回调执行; - 如果都不是,就直接返回一个已resolve的Promise,值就是item本身。
所以哪怕你给Promise.all传一堆字符串、数字,甚至像Sinon的FakeXMLHttpRequest这种普通对象,它也能正常执行——只是不会等待任何异步操作,直接进入then回调。
2. Sinon fakeServer的requests里到底是什么?
你用sinon.fakeServer.create()创建的server,它的requests数组存的是Sinon FakeXMLHttpRequest实例——这是Sinon模拟的XMLHttpRequest对象,用来拦截浏览器的真实请求。它确实不是标准Promise,所以你直接调用server.requests[0].then()会报错,因为它本身没有这个方法。
3. 你觉得“Promise.all(server.requests)能等待所有请求完成”,大概率是测试顺序的巧合
Sinon的fakeServer拦截请求后,并不会自动完成请求——你必须手动调用每个请求实例的.respond()方法,才能模拟请求成功/失败的状态。比如:
// 触发请求 store.dispatch(getDataThunk()); // 手动响应所有被拦截的请求 server.requests.forEach(req => { req.respond(200, {'Content-Type': 'application/json'}, JSON.stringify({data: 'test'})); }); // 这时候再做断言,或者等待action返回的Promise await store.dispatch(getDataThunk());
那为什么你用Promise.all(server.requests)感觉能正常工作?很大可能是你在测试中,是先手动调用了.respond(),再执行Promise.all的then回调——这时候看起来像是Promise.all在等待请求完成,但实际上请求已经被你手动处理完了,Promise.all只是刚好在这之后执行而已。
正确的做法:等待真实的Promise
如果你想等待请求处理完成,应该等待你的action creator返回的Promise,而不是Sinon的requests数组。比如你之前的代码里:
const requests = []; let getDataThunk = actions.getData(); requests.push(store.dispatch(getDataThunk)); // 这里dispatch返回的是Promise await Promise.all(requests); // 这才是真正等待请求完成的正确方式
而对于Sinon的fakeServer,它的作用是拦截请求、模拟响应,你需要在触发请求后,先手动调用.respond(),再等待action的Promise完成。
内容的提问来源于stack exchange,提问作者nonopolarity

