我有承诺的以下功能:
const ajaxRequest = (url) => {
return new Promise(function(resolve, reject) {
axios.get(url)
.then((response) => {
//console.log(response);
resolve(response);
})
.catch((error) => {
//console.log(error);
reject();
});
});
}
const xmlParser = (xml) => {
let { data } = xml;
return new Promise(function(resolve, reject) {
let parser = new DOMParser();
let xmlDoc = parser.parseFromString(data,"text/xml");
if (xmlDoc.getElementsByTagName("AdTitle").length > 0) {
let string = xmlDoc.getElementsByTagName("AdTitle")[0].childNodes[0].nodeValue;
resolve(string);
} else {
reject();
}
});
}
我正在尝试为JSON数组中的每个对象应用这些函数:
const array = [{"id": 1, "url": "www.link1.com"}, {"id": 1, "url": "www.link2.com"}]
我提出了以下解决方案:
function example() {
_.forEach(array, function(value) {
ajaxRequest(value.url)
.then(response => {
xmlParser(response)
.catch(err => {
console.log(err);
});
});
}
}
我想知道这个解决方案是否可以接受两件事:
在以下事项中对承诺申请forEach()是一种好习惯。
有没有更好的方法将先前的promise结果作为then()链中的参数传递? (我正在通过response
param)。
答案 0 :(得分:4)
您可以使用.reduce()
访问之前的Promise
。
function example() {
return array.reduce((promise, value) =>
// `prev` is either initial `Promise` value or previous `Promise` value
promise.then(prev =>
ajaxRequest(value.url).then(response => xmlParser(response))
)
, Promise.resolve())
}
// though note, no value is passed to `reject()` at `Promise` constructor calls
example().catch(err => console.log(err));
注意,Promise
函数不需要ajaxRequest
构造函数。
const ajaxRequest = (url) =>
axios.get(url)
.then((response) => {
//console.log(response);
return response;
})
.catch((error) => {
//console.log(error);
});
答案 1 :(得分:1)
您提供的代码的唯一问题是xmlParser的结果丢失,forEach循环只是迭代但不存储结果。为了保持结果,你需要使用Array.map作为结果得到Promise,然后Promise.all等待并将所有结果都放到数组中。
我建议使用ES2017的async / await来简化处理promises。由于提供的代码已经使用了箭头函数,这需要转换为旧版浏览器兼容性,因此您可以添加转换插件来支持ES2017。
在这种情况下,您的代码就像:
function example() {
return Promise.all([
array.map(async (value) => {
try {
const response = await ajaxRequest(value.url);
return xmlParser(response);
} catch(err) {
console.error(err);
}
})
])
}
以上代码将并行运行所有请求,并在所有请求完成后返回结果。您可能还希望逐个触发和处理请求,如果这是您的问题,还可以访问以前的承诺结果:
async function example(processResult) {
for(value of array) {
let result;
try {
// here result has value from previous parsed ajaxRequest.
const response = await ajaxRequest(value.url);
result = await xmlParser(response);
await processResult(result);
} catch(err) {
console.error(err);
}
}
}
答案 2 :(得分:1)
另一个解决方案是使用Promise.all
来执行此操作,我认为这是一个比循环ajax请求更好的解决方案。
const array = [{"id": 1, "url": "www.link1.com"}, {"id": 1, "url": "www.link2.com"}]
function example() {
return Promise.all(array.map(x => ajaxRequest(x.url)))
.then(results => {
return Promise.all(results.map(data => xmlParser(data)));
});
}
example().then(parsed => {
console.log(parsed); // will be an array of xmlParsed elements
});
答案 3 :(得分:0)
有没有更好的方法可以将之前的承诺结果传递给 then()链中的参数?
事实上,您可以按任何顺序和代码的任何位置链接和解决承诺。一个通用规则 - then
或catch
分支的任何链式承诺只是新的承诺,应该在以后链接。
但没有限制。使用循环时,最常见的解决方案是reduce
左侧foldl,但您也可以使用简单的let
- 变量重新分配新的承诺。
例如,您甚至可以设计延迟承诺链:
function delayedChain() {
let resolver = null
let flow = new Promise(resolve => (resolver = resolve));
for(let i=0; i<100500; i++) {
flow = flow.then(() => {
// some loop action
})
}
return () => {
resolver();
return flow;
}
}
(delayedChain())().then((result) => {
console.log(result)
})