Я не думаю, что подписка на нокаут поддерживает асин 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()" />