【问题标题】:Synchronisation problem using rxjs and old style callbacks使用 rxjs 和旧式回调的同步问题
【发布时间】:2020-07-23 17:56:10
【问题描述】:

我有带有 4 个异步函数的老式库 - 我们称它们为 libAlibBlibClibD(每个函数的运行时间未知/随机) - 当它们完成时,它们调用我的回调

我编写了 loadString 函数,它返回 observable(它应该包含每个回调函数的结果 - 为了简单起见,我在这里使用字符串,但它可以是结果数组)但它给出了错误的结果“AADDDDD”。预期结果是“AADDDDDCCCCBBB”。如何正确使用 rxjs?

function loadString() {
  let result = '';
  
  return Observable.create(observer =>{
    libA( n=> { result+='A'.repeat(n) });
    libB( n=> { result+='B'.repeat(n) });
    libC( n=> { result+='C'.repeat(n) });
    libD( n=> {
      result+='D'.repeat(n);
      observer.next(result);
    });        
  })
}

下面有工作的 sn-p,您可以将其复制到您的答案并开发 loadString 函数

// SET-UP
const { of, Observable } = rxjs;
const { map, switchMap, delay } = rxjs.operators;

// simulated lib function - not change this (the times are random)
function libA(callback) { setTimeout( _=>callback(2), 1000); } 
function libB(callback) { setTimeout( _=>callback(3), 3000); }
function libC(callback) { setTimeout( _=>callback(4), 2000); }
function libD(callback) { setTimeout( _=>callback(5), 1500); }


// QUESTION: how to write below function body using rxjs?
function loadString() {
  let result = '';
  
  return Observable.create(observer =>{
    libA( n=> { result+='A'.repeat(n) });
    libB( n=> { result+='B'.repeat(n) });
    libC( n=> { result+='C'.repeat(n) });
    libD( n=> {
      result+='D'.repeat(n);
      observer.next(result);
    });        
  })
}


// TEST
let s = loadString().subscribe(str=> {
   console.log(str); // wrong result: I expected "AADDDDDCCCCBBB" value 
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.min.js" integrity="sha256-85uCh8dPb35sH3WK435rtYUKALbcvEQFC65eg+raeuc=" crossorigin="anonymous"></script>

UPDAE

我不想按顺序运行 lib 函数,而是并行运行(它们向 API 发送一些请求...)

【问题讨论】:

  • 提示:如果 libD 先完成会怎样?
  • This question 可能有用。

标签: javascript rxjs


【解决方案1】:

一个说明您的问题的组合解决方案 - 您应该只在调用最后一次回调后调用 observer.next。只是保持运行计数。下面的代码并不重要,关键是“等到最后一个被调用后再发出”的知识。

这是一个有趣的解决方案:

// SET-UP
const { of, Observable } = rxjs;
const { map, switchMap, delay } = rxjs.operators;

// simulated lib function - not change this (the times are random)
function libA(callback) { setTimeout( _=>callback(2), 1000); } 
function libB(callback) { setTimeout( _=>callback(3), 3000); }
function libC(callback) { setTimeout( _=>callback(4), 2000); }
function libD(callback) { setTimeout( _=>callback(5), 1500); }


// QUESTION: how to write below function body using rxjs?
function loadString() {
  let result = '';

  const done = (observer,count) => val => {
      result += val;
      // THIS IF STATEMENT IS WHAT YOU WERE MISSING
      if(!--count) return observer.next(result);
  }
  
  return Observable.create(observer =>{
    const complete = done(observer,4);
    libA( n=> complete('A'.repeat(n))),
    libB( n=> complete('B'.repeat(n))),
    libC( n=> complete('C'.repeat(n))),
    libD( n=> complete('D'.repeat(n)));
  })
}


// TEST
let s = loadString().subscribe(str=> {
   console.log(str); // wrong result: I expected "AADDDDDCCCCBBB" value 
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.min.js" integrity="sha256-85uCh8dPb35sH3WK435rtYUKALbcvEQFC65eg+raeuc=" crossorigin="anonymous"></script>

【讨论】:

  • 也感谢您的回答 - 我想知道是否有某种 combineLatest 运算符可以保留任务完成时间顺序?
  • 我也找到了this 替代方案——也许这对你来说会很有趣
【解决方案2】:

从前一个的回调中调用每个libX 函数。

function loadString() {
  let result = '';

  return Observable.create(observer => {
    libA(n => {
      result += 'A'.repeat(n);
      libB(n => {
        result += 'B'.repeat(n);
        libC(n => {
          result += 'C'.repeat(n);
          libD(n => {
            result += 'D'.repeat(n);
            observer.next(result);
          });
        });
      });
    })
  });
}

【讨论】:

  • 这种方法的问题是它们是按顺序运行的——但在我的情况下,lib 函数会向 API 发送一些请求——我想并行运行它们——但谢谢你的回答
  • 您可以将 observables 转换为 Promise,然后使用 Promise.all() 按顺序获取结果。
  • 我看到你喜欢 mix/convert observable 到 promise,但是这种方法有一些缺点:它的可扩展性较差(如果你想使用例如 repeat(n) 请求或取消它......) .所以一般来说它不是很好的方法“NOTE: using toPromise() is an antipattern”~Ben Lesh rxjs 作者。但是,如果您有复杂的相互依赖的请求/任务链 - 最好使用 await approach like here - 但这种情况很少见
【解决方案3】:

libD 是唯一调用observer.next 的东西。一旦完成,即使其他的还没有完成,你的 observable 也会发出。所以你需要等待。试试combineLatest(虽然这不会保持秩序)?

// SET-UP
const { of, Observable, combineLatest } = rxjs;
const { map, switchMap, delay } = rxjs.operators;

// simulated lib function - not change this (the times are random)
function libA(callback) { setTimeout( _=>callback(2), 1000); } 
function libB(callback) { setTimeout( _=>callback(3), 3000); }
function libC(callback) { setTimeout( _=>callback(4), 2000); }
function libD(callback) { setTimeout( _=>callback(5), 1500); }


// QUESTION: how to write below function body using rxjs?
function loadString() {
  let result = '';
   
   return combineLatest(
     Observable.create(observer => libA(n => observer.next('A'.repeat(n)))),
     Observable.create(observer => libB(n => observer.next('B'.repeat(n)))),   
     Observable.create(observer => libC(n => observer.next('C'.repeat(n)))),          
     Observable.create(observer => libD(n => observer.next('D'.repeat(n))))
   );
 
}


// TEST
let s = loadString().subscribe(str=> {
   console.log(str); // wrong result: I expected "AADDDDDCCCCBBB" value 
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.min.js" integrity="sha256-85uCh8dPb35sH3WK435rtYUKALbcvEQFC65eg+raeuc=" crossorigin="anonymous"></script>

【讨论】:

  • 感谢您的回答-它有很大帮助-但我们在这里松散了顺序(我想知道哪个结果是第一个,哪个是最后一个)-我是 rxjs 初学者,所以不清楚我该怎么做:(
【解决方案4】:

我们也可以使用bufferCount(技术来自here

// SET-UP
const { Subject } = rxjs;
const { take, bufferCount, map, finalize } = rxjs.operators;

// simulated lib function - not change this (the times are random)
function libA(callback) { setTimeout( _=>callback(2), 1000); } 
function libB(callback) { setTimeout( _=>callback(3), 3000); }
function libC(callback) { setTimeout( _=>callback(4), 2000); }
function libD(callback) { setTimeout( _=>callback(5), 1500); }


function loadString() {
  let result = new Subject();
  
  libA( n=> { result.next('A'.repeat(n)); });     
  libB( n=> { result.next('B'.repeat(n)); });     
  libC( n=> { result.next('C'.repeat(n)); });     
  libD( n=> { result.next('D'.repeat(n)); });        

  return result.pipe(
    bufferCount(4),       // num of callbacks
    take(1),              // this will unsubscribe after 4 values received
    map( r => r.join`` ),
  );
}


// TEST
let s = loadString().subscribe(str=> {
   console.log(str); // wrong result: I expected "AADDDDDCCCCBBB" value 
}, e=>{}, ()=> console.log('final'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.min.js" integrity="sha256-85uCh8dPb35sH3WK435rtYUKALbcvEQFC65eg+raeuc=" crossorigin="anonymous"></script>

或效率较低的scan(因为它连接字符串不是一次而是每次某些任务结束时)

// SET-UP
const { Subject } = rxjs;
const { scan, take, last, map } = rxjs.operators;

// simulated lib function - not change this (the times are random)
function libA(callback) { setTimeout( _=>callback(2), 1000); } 
function libB(callback) { setTimeout( _=>callback(3), 3000); }
function libC(callback) { setTimeout( _=>callback(4), 2000); }
function libD(callback) { setTimeout( _=>callback(5), 1500); }


function loadString() {
  let result = new Subject();
  
  libA( n=> { result.next('A'.repeat(n)); });     
  libB( n=> { result.next('B'.repeat(n)); });     
  libC( n=> { result.next('C'.repeat(n)); });     
  libD( n=> { result.next('D'.repeat(n)); });        

  return result.pipe(
      take(4),
      scan((acc, value) => acc + value),
      last()
  );
}


// TEST
let s = loadString().subscribe(str=> {
   console.log(str); // wrong result: I expected "AADDDDDCCCCBBB" value 
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.min.js" integrity="sha256-85uCh8dPb35sH3WK435rtYUKALbcvEQFC65eg+raeuc=" crossorigin="anonymous"></script>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-11
    • 2015-07-30
    • 2020-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多