admin管理员组文章数量:1402016
For example:
Get 5 pages in parrallel using jquery ajax. When page2 return, do nothing. When page1 return, do something with page1 and page2.
// assume there is some operator that can do this,
// then it might look like this?
Rx.Observable.range(1, 5).
someOperator(function(page) {
return Rx.Observable.defer( () => $.get(page) );
}).scan(function(preVal, curItem) {
preVal.push(curItem);
return preVal;
}, []);
For example:
Get 5 pages in parrallel using jquery ajax. When page2 return, do nothing. When page1 return, do something with page1 and page2.
// assume there is some operator that can do this,
// then it might look like this?
Rx.Observable.range(1, 5).
someOperator(function(page) {
return Rx.Observable.defer( () => $.get(page) );
}).scan(function(preVal, curItem) {
preVal.push(curItem);
return preVal;
}, []);
Share
Improve this question
asked Dec 28, 2015 at 2:17
ChefChef
6837 silver badges17 bronze badges
1
- Not sure about rxjs since I've never used it but here's a solution in pure javascript (no libraries): stackoverflow./questions/4631774/… – slebetman Commented Dec 28, 2015 at 2:41
2 Answers
Reset to default 3There exists the forkJoin
operator which will run all observable sequences in parallel and collect their last elements.
(quoted from the documentation). But if you use that one, you will have to wait for all 5 promises to resolve, or one of the 5 to be in error. It is the close equivalent to RSVP.all
or jQuery.when
. So that would not allow you to do something once you have the second. I mention it anyways in case it might be useful to you in another case.
Another possibility is to use concatMap
which will allow you to receive the resolved promises in order. However, I don't have it clear that they will be launched in parallel, the second promise should start only when the first one has resolved.
Last option I can think about is to use merge(2)
, that should run two promises in parallel, and at anytime they will only be two promises being 'launched'.
Now if you don't use defer
, and you use concatMap
, I believe you should have all AJAX request started, and still the right ordering. So you could write :
.concatMap(function(page) {
return $.get(page);
})
Relevant documentation:
- merge(maxConcurrency)
- concatMap
- forkJoin
concatMap keeps the order, but processes elements in sequence.
mergeMap does not keep the order, but it runs in parallel.
I've created the operator mergeMapAsync below to make process elements (e.g. page downloads) in parallel but emit in order. It even supports throttling (e.g. download max. 6 pages in parallel).
Rx.Observable.prototype.mergeMapAsync = mergeMapAsync;
function mergeMapAsync(func, concurrent) {
return new Rx.Observable(observer => {
let outputIndex = 0;
const inputLen = this.array ? this.array.length : this.source.array.length;
const responses = new Array(inputLen);
const merged = this.map((value, index) => ({ value, index })) // Add index to input value.
.mergeMap(value => {
return Rx.Observable.fromPromise(new Promise(resolve => {
console.log(`${now()}: Call func for ${value.value}`);
// Return func retVal and index.
func(value.value).then(retVal => {
resolve({ value: retVal, index: value.index });
});
}));
}, concurrent);
const mergeObserver = {
next: (x) => {
console.log(`${now()}: Promise returned for ${x.value}`);
responses[x.index] = x.value;
// Emit in order using outputIndex.
for (let i = outputIndex, len = responses.length; i < len; i++) {
if (typeof responses[i] !== "undefined") {
observer.next(responses[i]);
outputIndex = i + 1;
} else {
break;
}
}
},
error: (err) => observer.error(err),
plete: () => observer.plete()
};
return merged.subscribe(mergeObserver);
});
};
// ----------------------------------------
const CONCURRENT = 3;
var start = Date.now();
var now = () => Date.now() - start;
const array = ["a", "b", "c", "d", "e"];
Rx.Observable.from(array)
.mergeMapAsync(value => getData(value), CONCURRENT)
.finally(() => console.log(`${now()}: End`))
.subscribe(value => {
console.log(`${now()}: ${value}`); // getData
});
function getData(input) {
const delayMin = 500; // ms
const delayMax = 2000; // ms
return new Promise(resolve => {
setTimeout(() => resolve(`${input}+`), Math.floor(Math.random() * delayMax) + delayMin);
});
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>mergeMapAsync</title>
<script src="https://cdnjs.cloudflare./ajax/libs/rxjs/5.5.8/Rx.min.js"></script>
</head>
<body>
</body>
</html>
本文标签: javascriptFire async request in parallel but get result in order using rxjsStack Overflow
版权声明:本文标题:javascript - Fire async request in parallel but get result in order using rxjs - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744314041a2600172.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论