【问题标题】:*RxJs* How to combine multiple ajax to one observable that doesn't require sequence*RxJs* 如何将多个 ajax 组合成一个不需要序列的 observable
【发布时间】:2017-12-25 02:20:05
【问题描述】:

我有两个 API 来获取 Angular5 上的菜单数据,但我不知道我是否使用嵌套订阅 getSecondMenu 函数是 RxJs 的正确方式。有谁知道,下面是预期的结果和示例代码:

console.log: 1. get firstMenuList 2. all done 3. get secondMenu * 3

open on jsFiddle

getFirstMenu$()
  .do(firstMenuList => {
    console.log('get firstMenuList');
  })
  .do(firstMenuList => {
    firstMenuList.forEach(firstMenu => {
      // nested subscribe : bind to child property, let Angular auto-generate second Menu in HTML
      getSecondMenu$(firstMenu.ID).subscribe((secondMenu) => {
        firstMenu.child = secondMenu;
        console.log('get secondMenu');
      });
    })
  })
  .subscribe((menuList) => {
    console.log('all done' );
  });


function getFirstMenu$() {
  return Rx.Observable.of([{
    ID: 'menu1',
    child: null
  }, {
    ID: 'menu2',
    child: null
  }, {
    ID: 'menu3',
    child: null
  }, ]);
}

function getSecondMenu$(menuID) {
  let source = null;
  switch (menuID) {
    case 'menu1':
      source = [{
        ID: 'subMenu1-1',
      }];
      break;
    case 'menu2':
      source = [{
        ID: 'subMenu2-1',
      }];
      break;
    default:
      source = [];
      break;
  }
  const delayTime = ((Math.random() * 20) + 5) * 120;
  return Rx.Observable.of(source).delay(delayTime);
}
<script src="https://npmcdn.com/@reactivex/rxjs@5.0.0-beta.8/dist/global/Rx.umd.js"></script>

结论

谢谢,@Fan Cheung 谈了很多关于如何组合多个 observable,总结一下你有两种方法可以做到这一点

使用回调

// componnet
class myComponent {
  getMenu() {
    new sharedService().getMenu({
      getFirstMenuList: () => {
        console.log('render first menu to template...')
      }
    })
  }
}

// shared service
class sharedService {
  getMenu(callback) {
    this.getFirstMenu$()
      .mergeMap(firstMenuList => {
        console.log('get first menu');
        //
        if (callback.getFirstMenuList) {
          callback.getFirstMenuList(firstMenuList);
        }

        return Rx.Observable.from(firstMenuList);
      })
      .mergeMap(firstMenu => this.getSecondMenu$(firstMenu.ID), (firstMenu, secondMenu) => {
        //
        console.log('get second menu');
        //
        firstMenu.child = secondMenu
        return firstMenu;
      })
      .reduce((acc, curr) => acc.concat(curr), [])
      .subscribe((menuList) => {
        console.log('all done');
      });
  }

  getFirstMenu$() {
    return Rx.Observable.of([{
      ID: 'menu1',
      child: null
    }, {
      ID: 'menu2',
      child: null
    }, {
      ID: 'menu3',
      child: null
    }, ]);
  }

  getSecondMenu$(menuID) {
    let source = null;
    switch (menuID) {
      case 'menu1':
        source = [{
          ID: 'subMenu1-1',
        }];
        break;
      case 'menu2':
        source = [{
          ID: 'subMenu2-1',
        }];
        break;
      default:
        source = [];
        break;
    }
    const delayTime = ((Math.random() * 20) + 5) * 120;
    return Rx.Observable.of(source).delay(delayTime);
  }
}

// getTemplate
new myComponent().getMenu();
<script src="https://npmcdn.com/@reactivex/rxjs@5.0.0-beta.8/dist/global/Rx.umd.js"></script>

返回其他函数订阅

// componnet
class myComponent {
  getMenu() {
    new sharedService().getMenu().subscribe(() => {
        console.log('render first menu to template...');      
    })
  }
}

// shared service
class sharedService {
  getMenu() {
    return this.getFirstMenu$()
      .mergeMap(firstMenuList => {
        console.log('get first menu');
        return Rx.Observable.from(firstMenuList);
      })
      .mergeMap(firstMenu => this.getSecondMenu$(firstMenu.ID), (firstMenu, secondMenu) => {
        //
        console.log('get second menu');
        //
        firstMenu.child = secondMenu
        return firstMenu;
      })
      .reduce((acc, curr) => acc.concat(curr), [])
      .do((menuList) => {
        console.log('all done');
      });
  }

  getFirstMenu$() {
    return Rx.Observable.of([{
      ID: 'menu1',
      child: null
    }, {
      ID: 'menu2',
      child: null
    }, {
      ID: 'menu3',
      child: null
    }, ]);
  }

  getSecondMenu$(menuID) {
    let source = null;
    switch (menuID) {
      case 'menu1':
        source = [{
          ID: 'subMenu1-1',
        }];
        break;
      case 'menu2':
        source = [{
          ID: 'subMenu2-1',
        }];
        break;
      default:
        source = [];
        break;
    }
    const delayTime = ((Math.random() * 20) + 5) * 120;
    return Rx.Observable.of(source).delay(delayTime);
  }
}

// getTemplate
new myComponent().getMenu();
<script src="https://npmcdn.com/@reactivex/rxjs@5.0.0-beta.8/dist/global/Rx.umd.js"></script>

【问题讨论】:

  • ForEach .subscribe 看起来像是一种确保内存泄漏的可靠方法。即使没有嵌套的第二次调用
  • @c69 有什么好的方法可以避免内存泄漏?使用平面地图?

标签: javascript angular rxjs rxjs5


【解决方案1】:

试试这个

getFirstMenu$()
.map(firstMenuList => Observable.from(firstMenuList))
.flatMap(firstMenu=> getSecondMenu$(firstMenu.ID))
.do(secondMenu => firstMenu.child = secondMenu)
.subcribe()

编辑1

getFirstMenu$()
.mergeMap(firstMenuList => Rx.Observable.from(firstMenuList))
.mergeMap(firstMenu=> getSecondMenu$(firstMenu.ID)
,(firstMenu, secondMenu) => {
 firstMenu.child=secondMenu
 return firstMenu
 })
.reduce((acc,curr)=>  acc.concat(curr) ,[])
.subscribe((menuList) => {
 console.log(menuList)
 });

【讨论】:

  • 在这个示例中我不知道我应该如何获取 menuList 数据,我在这个jsFiddle 上尝试一下,你能帮我更正吗?
  • 你可以模拟 getFirstMenu$=()=>Observable.of([{lable:'one'},{lable:'two'}]) 并在小提琴中使用它
  • 我在 jsFiddle 中使用过 Observable.of,你可以在这个链接上查看它jsfiddle.net/jkj1f3k9
  • jsfiddle.net/fancheung/rjL8dwyv 分叉并更新了你的小提琴
  • 感谢您的回答,我在预期结果中添加了一个时间线,希望它能让我的问题变得简单,您会检查这个image 和这个jsFiddle
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-16
  • 1970-01-01
相关资源
最近更新 更多