例如:
使用jquery ajax在parrallel中获取5页。当page2返回时,什么都不做。当page1返回时,使用page1和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;
}, []);
答案 0 :(得分:2)
存在forkJoin
运算符run all observable sequences in parallel and collect their last elements.
(引自文档)。但是如果你使用那个,你将不得不等待所有5个承诺解决,或者5个中的一个出错。它相当于RSVP.all
或jQuery.when
。因此,一旦你拥有第二个,就不会允许你做某事。无论如何我都会提到它,以防它在另一种情况下对你有用。
另一种可能性是使用concatMap
,这将允许您按顺序接收已解决的承诺。但是,我不清楚它们是否会并行启动,第二个承诺应该在第一个承诺解决后开始。
我能想到的最后一个选项是使用merge(2)
,它应该并行运行两个承诺,并且在任何时候它们只会被两个承诺“启动”。
现在,如果您不使用defer
,并且使用concatMap
,我相信您应该启动所有AJAX请求,并且仍然是正确的排序。所以你可以写:
.concatMap(function(page) {
return $.get(page);
})
相关文件:
答案 1 :(得分:1)
concatMap保持顺序,但按顺序处理元素。
mergeMap不保留顺序,但它并行运行。
我在下面创建了运算符mergeMapAsync,以便并行生成流程元素(例如页面下载),但按顺序发出。它甚至支持限制(例如,并行下载最多6页)。
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),
complete: () => observer.complete()
};
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.com/ajax/libs/rxjs/5.5.8/Rx.min.js"></script>
</head>
<body>
</body>
</html>