Нокаут JS, возвращающий обещание от pureComputed - PullRequest
1 голос
/ 20 марта 2020

У меня есть функция pureComputed, и я хотел бы разрешить внутри нее обещание, однако при ссылке на promotionPrice в шаблоне оно отображает обещание нерешенным ([object Promise]). Есть идеи, где я иду не так? После отладки я чувствую, что правильно решаю обещание в коде ...

    this.promotionPrice = ko.pureComputed({
        read: async () => {
            const regionPrice = this.getRegionPrices().then(regions => _.meanBy(regions, region => region.price));
            return await regionPrice;
        },
        write: (newValue) => {
            // Relevant write code here
        }
    }).extend({ notify: 'always' });

В шаблоне HTML ...

<input type="text" class="form-control spaced-select" data-bind="value: promotionPrice">

1 Ответ

2 голосов
/ 20 марта 2020

Я не думаю, что подписка на нокаут поддерживает асин c методы чтения. Вместо этого вы можете использовать наблюдаемую поддержку вашей вычисленной записи:

const val = ko.observable("loading");
const fetchedWhenNeeded = ko.pureComputed({
  read: function() {
    someApiCall().then(val);
    return val();
  }
});

ko.applyBindings({ fetchedWhenNeeded });

function someApiCall () {
  return new Promise(
    (res, rej) => {
      setTimeout(() => res(42), 750)
    }
  );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<h1 data-bind="text: fetchedWhenNeeded"></h1>

Может быть, стоит выделить этот код для помощника и подумать об обработке ошибок. Например:

const ObsFromAsyncGetter = (getDataAsync, initialValue = null) => {
  let triggered = false;
  const value = ko.observable(initialValue);
  
  return ko.pureComputed({
    read: () => {
      if (!triggered) {
        getDataAsync().then(value); // TODO: how will you handle errors?
        triggered = true;
      }
      
      return value();
    }
  });
}


const myValue = ObsFromAsyncGetter(someApiCall, "loading...");
console.log("Observable constructed");

// Note: API call is not yet made because nothing needs myValue
console.log("Applying bindings");
ko.applyBindings({ myValue });

// Note: API call only triggers once, even though it has three dependencies
console.log("Applied bindings");


function someApiCall () {
  console.log("API call made");
  return new Promise(
    (res, rej) => {
      setTimeout(() => res(42), 750)
    }
  );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<h1 data-bind="text: myValue"></h1>
<h2 data-bind="text: myValue"></h2>

Если причина, по которой вы задаете этот вопрос, на самом деле не в ленивом извлечении данных, основанных на зависимостях выбивки, а скорее в управлении состоянием сервера и клиента, я думаю, вам лучше создать две простые наблюдаемые и выполнить начальную выборку в вашей модели представления. Что-то вроде:

JS

const loading = ko.observable(true);
const serverState = ko.observable(null);
const clientState = ko.observable("");

const updateStateWithServerData = data => {
  serverState(data);
  clientState(data);
};

// Init
fetchData()
  .then(updateStateWithServerData)
  .finally(() => loading(false));

// Update after typing
clientState.subscribe(newValue => {
  loading(true);
  postData(newValue)
    .then(updateStateWithServerData)
    .finally(() => loading(false));
});

ko.applyBindings({ clientState, loading });

HTML:

<input data-bind="value: clientState, enable: !loading()" />
...