javascript - async/await 循环在 axios.get 中按预期工作,但不适用于所需的本地 .json 文件
问题描述
我真的很想知道我在这里缺少什么 javascript 概念(关于 async/await)。我很确定这不是一个重复的问题。
我的代码涉及太多,无法作为示例显示,因此我将尝试尽我所能描述它并以最简单的形式显示问题。这段代码的主要目的是并行发出一组网络请求,并在它们都完成后执行一些操作。我让它正常工作,循环执行“似乎”暂停,直到返回等待的值,这是需要的。
但是,当我使用本地 .json 文件(通过 加载require
)而不是 usingaxios.get
时,循环会在返回等待的值之前一直运行。这是有问题的,因为我在循环暂停的前提下改变了等待的值。
/*
Simplified as much as possible.
Note: The code works as desired when globalOptions.useNetworkStub = false
*/
const axios = require('axios').default;
const globalOptions = {
useNetworkStub: true
}
const getSearchByTerm = async(term) => {
if (globalOptions.useNetworkStub) {
const networkStub = require('./fake-response.json')
return Promise.resolve(networkStub)
}
return axios.get('https://jsonplaceholder.typicode.com/todos/1')
}
const getSearchesByTerms = async(terms = ['cats','dogs']) => {
const results = []
let result
try {
for (let i = 0; i < terms.length; i++) {
const term = terms[i]
result = await getSearchByTerm(term)
result.data && (result.data.searchTerm = term) // The issue is here!
results.push(result)
}
} catch (err) {
return Promise.reject(`getSearchesByTerms() Failed: ${err}`)
}
// ... code truncated here to keep things simple ...
return Promise.resolve(results)
}
getSearchesByTerms()
.then((responses) => {
let merged = {responses: []}
for (const response of responses) {
merged.responses.push(response.data)
}
console.log(`SUCCESS, data: ${JSON.stringify(merged, null, 2)}`)
})
.catch(e=>console.log(e))
假响应.json
{
"data": {
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
}
正如我之前提到的,当使用 axios 时,最终结果是正确的。第一个响应有一个键值对,searchTerm: 'cats'
第二个响应有一个键值对searchTerm: 'dogs'
当使用本地 .json 文件时,第一个和第二个响应都具有相同的键值对searchTerm: 'dogs'
. 这就是问题。
编辑:更改const term = terms[i].term
为const term = terms[i]
另一个编辑:修复代码中的拼写错误,添加数据并在 repl.it 上发布此fake-response.json
问题的工作示例
解决方案
减去一些拼写错误,您的代码可以运行——但存在一些问题。
// example.js
const axios = require('axios').default;
const globalOptions = {
useNetworkStub: true
}
const getSearchByTerm = async function(term) {
if (globalOptions.useNetworkStub) {
const networkStub = require('./fake-response.json')
return Promise.resolve(networkStub)
}
return axios.get('https://some-live-endpoint.com/', {params:{q: term}})
}
const getSearchesByTerms = async function(terms = ['cats','dogs']) {
const results = []
let result
try {
for (let i = 0; i < terms.length; i++) {
const term = terms[i]
result = await getSearchByTerm(term)
console.log("Term: ", term, "Result: ", result); // added this log to clarify your issue
result.data && (result.data.searchTerm = term) // The issue is here!
results.push(result)
}
} catch (err) {
return Promise.reject(`getSearchesByTerms() Failed: ${err}`)
}
// ... code truncated here to keep things simple ...
return Promise.resolve(results)
}
getSearchesByTerms()
.then((responses) => {
let merged = {responses: []}
for (const response of responses) {
merged.responses.push(response.data)
}
console.log(`SUCCESS, data: ${JSON.stringify(merged, null, 2)}`)
})
.catch(e=>console.log(e))
这是和示例 json 文件
{
"data": {
"payload": "Success"
}
}
这是您将获得的输出:
Term: cats Result: { data: { payload: 'Success' } }
Term: dogs Result: { data: { payload: 'Success', searchTerm: 'cats' } }
SUCCESS, data: {
"responses": [
{
"payload": "Success",
"searchTerm": "dogs"
},
{
"payload": "Success",
"searchTerm": "dogs"
}
]
}
需要注意的是,您的问题不在于async
两个结果都使用对同一对象的引用。这是一堂课,可以让您在 Javascript 和许多其他语言中以许多微妙但重要的方式烧伤您,这些语言对程序员隐藏了指针的复杂性。您通常应该避免改变对象。
这是一个使用 JS 传播语法来复制对象而不是变异的版本。
const axios = require('axios').default;
const globalOptions = {
useNetworkStub: true
}
const getSearchByTerm = async function(term) {
if (globalOptions.useNetworkStub) {
const networkStub = require('./fake-response.json')
return Promise.resolve(networkStub)
}
return axios.get('https://some-live-endpoint.com/', {params:{q: term}})
}
const getSearchesByTerms = async function(terms = ['cats','dogs']) {
const results = []
let result
try {
for (let i = 0; i < terms.length; i++) {
const term = terms[i]
result = await getSearchByTerm(term)
console.log("Term: ", term, "Result: ", result); // added this log to clarify your issue
if (result && "data" in result) {
results.push({ data: { ...result.data, term }}) // copies instead of mutating original object
}
}
} catch (err) {
return Promise.reject(`getSearchesByTerms() Failed: ${err}`)
}
// ... code truncated here to keep things simple ...
return Promise.resolve(results)
}
getSearchesByTerms()
.then((responses) => {
let merged = {responses: []}
for (const response of responses) {
merged.responses.push(response.data)
}
console.log(`SUCCESS, data: ${JSON.stringify(merged, null, 2)}`)
})
.catch(e=>console.log(e))
这是一个以您希望它工作的方式发生变异的版本。重要的变化是存根有多个可以查询的对象:
// newExample.js
// gets a new object each time, so mutation doesn't break
const axios = require('axios').default;
const globalOptions = {
useNetworkStub: true
}
const getSearchByTerm = async function(term) {
if (globalOptions.useNetworkStub) {
const networkStub = require('./fake-response.json')[term]
return Promise.resolve(networkStub)
}
return axios.get('https://some-live-endpoint.com/', {params:{q: term}})
}
const getSearchesByTerms = async function(terms = ['cats','dogs']) {
const results = []
let result
try {
for (let i = 0; i < terms.length; i++) {
const term = terms[i]
result = await getSearchByTerm(term)
console.log("Term: ", term, "Result: ", result); // added this log to clarify your issue
result.data && (result.data.searchTerm = term) // no longer mutating same object
results.push(result)
}
} catch (err) {
return Promise.reject(`getSearchesByTerms() Failed: ${err}`)
}
// ... code truncated here to keep things simple ...
return Promise.resolve(results)
}
getSearchesByTerms()
.then((responses) => {
let merged = {responses: []}
for (const response of responses) {
merged.responses.push(response.data)
}
console.log(`SUCCESS, data: ${JSON.stringify(merged, null, 2)}`)
})
.catch(e=>console.log(e))
// fake-response.json
{
"cats": {
"data": {
"payload": "Success for cats!"
}
},
"dogs": {
"data": {
"payload": "Success for dogs!"
}
}
}
仍然。如果你担心深度克隆——我建议你以一种不需要改变或克隆值的方式来规划你的输出:
// better.js
// plans output to not mutate or copy
const axios = require('axios').default;
const globalOptions = {
useNetworkStub: true
}
const getSearchByTerm = async function(term) {
if (globalOptions.useNetworkStub) {
const networkStub = require('./fake-response.json')[term]
return Promise.resolve(networkStub)
}
return axios.get('https://some-live-endpoint.com/', {params:{q: term}})
}
const getSearchesByTerms = async function(terms = ['cats','dogs']) {
const results = []
let result
try {
for (let i = 0; i < terms.length; i++) {
const term = terms[i]
result = await getSearchByTerm(term)
console.log("Term: ", term, "Result: ", result); // added this log to clarify your issue
if (result && "data" in result) {
results.push({ term, data: result.data }) // doesn't copy or mutate result
}
}
} catch (err) {
return Promise.reject(`getSearchesByTerms() Failed: ${err}`)
}
// ... code truncated here to keep things simple ...
return Promise.resolve(results)
}
getSearchesByTerms()
.then((responses) => {
let merged = {responses: []}
for (const response of responses) {
merged.responses.push(response) // grabbing while response
}
console.log(`SUCCESS, data: ${JSON.stringify(merged, null, 2)}`)
})
.catch(e=>console.log(e))
推荐阅读
- vb.net - Visual Basic shell32.shell
- spring-cloud-dataflow - Spring Cloud 数据流浏览器控制台报错
- coq - 当您有多行定义归纳构造函数的操作时,这意味着什么?
- javascript - 简写 if-else 在 Javascript 中返回错误
- apache-kafka - Kafka 中的“__consumer_offsets”主题是什么
- node.js - Node.js 分号使用
- javascript - 单击后更改图像,但将其他选项卡重置为初始图像 javascript
- javascript - 如何在事件发生后使用 React 滚动到元素?
- javascript - 如何在作为样式组件创建的自定义 React 模态中检测“onClickOutside”和“onEscapeKeyPress”
- microsoft-graph-api - 缺少网络挂钩通知消息