rxjs - 丢弃未来的返回可观察量

时间:2017-08-22 06:00:04

标签: javascript rxjs

所以这是我的可观察代码:

  var suggestions =
        Rx.Observable.fromEvent(textInput, 'keyup')
          .pluck('target','value')
          .filter( (text) => {
              text = text.trim();
              if (!text.length) // empty input field
              {
                  this.username_validation_display("empty");
              }
              else if (!/^\w{1,20}$/.test(text))
              {
                  this.username_validation_display("invalid");
                  return false;
              }
              return text.length > 0;
          })
          .debounceTime(300)
          .distinctUntilChanged()
          .switchMap(term => {
              return $.ajax({
                type: "post",
                url: "src/php/search.php",
                data: {
                  username: term,
                  type: "username"
                }
              }).promise();
            }
          );
  suggestions.subscribe(
    (r) =>
    {
        let j = JSON.parse(r);
        if (j.length)
        {
            this.username_validation_display("taken");
        }
        else
        {
            this.username_validation_display("valid");
        }
    },
    function (e)
    {
        console.log(e);
    }
  );

我遇到的问题是当输入为空时我有另一段代码基本上会返回错误:空输入'但它被返回的observable覆盖了。所以我想知道如果text.length为0,是否有办法忽略所有可观察量,但是当文本长度不为零时重新订阅。

我已经考虑过unsubscribe,但不知道在哪里尝试。

3 个答案:

答案 0 :(得分:8)

也许这会奏效吗?我们的想法是在switchmap中加入空输入,以便关闭当前的observable,即在这种情况下也将其丢弃。

var res = $scope.cat.filter(function(item){
  return item.parentid === categories[i].id;
});

if(res.length)
{
    $scope.content.push(categories[i]);
} 

答案 1 :(得分:5)

我建议在文本输入流之后移动debounceTime调用,并在去抖动流上进行过滤。这样一个有效但过时的值不会潜入ajax调用。此外,我们可能需要取消"当一个新的,可能无效的输入发生时,我们不希望"无效"要使用正在进行的异步调用的结果覆盖的状态。 takeUntil可以为此提供帮助。另请注意,可以使用Rx.Observable.ajax()代替$.ajax()

顺便说一句,拥有一个专门的"验证状态流可能很方便"这样this.username_validation_display(validationStatus)就在subscribe内的一个地方完成。

我想它是这样的:

const term$ = Rx.Observable.fromEvent(textInput, "keyup")
    .pluck("target", "value")
    .map(text => text.trim())
    .distinctUntilChanged()
    .share();

const suggestion$ = term$
    .debounceTime(300)
    .filter(term => getValidationError(term).length === 0)
    .switchMap(term => Rx.Observable
        .ajax({
            method: "POST",
            url: "src/php/search.php",
            body: {
                username: term,
                type: "username"
            }
        })
        .takeUntil(term$)
    )
    .map(result => JSON.parse(result));

const validationStatus$ = Rx.Observable.merge(
    term$.map(getValidationError),
    suggestion$.map(getSuggestionStatus));

// "empty", "invalid", "taken", "valid" or ""
validationStatus$
    .subscribe(validationStatus => this.username_validation_display(validationStatus));

// Helper functions
function getValidationError(term) {
    if (term.length === 0) {
        return "empty";
    }

    if (!/^\w{1,20}$/.test(term)) {
        return "invalid";
    }

    return "";
}

function getSuggestionStatus(suggestion) {
    return suggestion.length > 0 ? "taken" : "valid";
}

上述代码的行为在某种意义上也是不同的,即在用户输入有效输入的时间点与建议从服务器到达的时间点之间,username_validation_display将具有空值,即除了"空","无效","采取"和"有效"状态也会有一个中间空状态,可以用UI中的某些进度指示替换。

更新:我还可以考虑一种应该具有等效行为的替代方法,但相应的代码可能看起来更简单:

const username$ = Rx.Observable.fromEvent(textInput, "keyup")
    .pluck("target", "value")
    .map(text => text.trim())
    .distinctUntilChanged();

const validationStatus$ = username$.switchMap(username => {
    const validationError = getValidationError(username);
    return validationError ?
        Rx.Observable.of(validationError) :
        Rx.Observable.timer(300)
            .switchMapTo(getAvailabilityStatus$(username))
            .startWith("pending");
});

// "invalid", "empty", "taken", "valid" or "pending"
validationStatus$.subscribe(
    validationStatus => this.username_validation_display(validationStatus));

// Helper functions
function getAvailabilityStatus$(username) {
    return Rx.Observable
        .ajax({
            method: "POST",
            url: "src/php/search.php",
            body: {
                username: username,
                type: "username"
            }
        })
        .map(result => JSON.parse(result))
        .map(suggestions => suggestions.length > 0 ? "taken" : "valid");
}

function getValidationError(username) {
    if (username.length === 0) {
        return "empty";
    }

    if (!/^\w{1,20}$/.test(username)) {
        return "invalid";
    }

    return null;
}

这样我们可以同步检查每个不同的输入。如果输入为空或无效(由getValidationError()调用确定),我们会立即发出验证错误。否则我们立即发出"待定" status并启动一个计时器,该计时器将在300 ms后产生异步用户名可用性检查。如果在计时器结束或服务器到达可用性状态之前到达新文本,我们取消内部流并重新开始验证,这要归功于switchMap。所以它应该与debounceTime几乎相同。

答案 2 :(得分:1)

您可以将原始值流用作ajax调用的通知程序以中止其值。如果字段为空而没有被先前的ajax结果覆盖,这应该为您提供足够的控制来添加显示消息的附加处理。

const inputValues = Rx.Observable.fromEvent(document.getElementById("inputField"), "keyup")
  .pluck("target","value")
  .distinctUntilChanged()
  .do(val => console.log('new value: ' + val));


inputValues
  .filter(val => val.length > 0)
  .switchMap(text => doAjaxCall(text)
    .takeUntil(inputValues)
  )
  .subscribe(val => console.log('received a value: ' + val));


function doAjaxCall(inputValue) {
  return Rx.Observable.of(inputValue)
    .do(val => console.log('ajax call starting for: ' + val))
    .delay(1000)
    .do(val => console.log('ajax call returned for: ' + val));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.2/Rx.js"></script>
<label>input field<input type="text" id="inputField" /></label>