Страницы

Поиск по вопросам

суббота, 16 марта 2019 г.

Javascript: коллбэки, асинхронность и разделение кода

Здравствуйте.
Есть cordova приложение. Есть плагин, который я могу вызвать со стороны javascript, чтобы получить данные извне, например, через такой враппер:
CordovaWrapper.getRawData(successCallback, errorCallback);
Данные, которые я получу из cordova (они придут как аргументы successCallback), я бы хотел использовать в HTML приложении.
Конечно, можно во всех местах, где нужны данные из successCallback, использовать этот CordovaWrapper, чтобы получить данные и сразу же использовать:
CordovaWrapper.getRawData(function(JSONString) { var data = JSON.parse(JSONString) for (var i in data) { data[i].formattedValue = ''+data[i].value+''; }
// здесь мы используем эти полученные и отформатированные данные
}, function(e) { console.log(e) });
Но мне это не кажется хорошей идеей. Кроме того, хорошо бы было закешировать распаршенный JSON с примененным к нему форматированием.
Я мало знаком с Javascript, но мне хотелось бы реализовать какой-нибудь провайдер данных, к которому я смогу обращаться, чтобы получать закешированные и отформатированные данные из cordova (используя этот гипотетический CordovaWrapper). И в случае работы приложения в браузере, а не на девайсе, отдавать какие-нибудь захардкоженые данные.
if (standaloneMode) { // какие-то тестовые данные return [{ value: 'test1', options: [...], }, { value: 'test2', options: [...], }]; } else { return dataFromCordova; }
Проблема в том, что получение данных - асинхронное.
Если делать асинхронно, получается коллбэк на коллбэке:
Объявление:
DataProvider.getData = function(callback) { var format = function (arr) { for (var i in arr) { arr[i].formattedValue = ''+arr[i].value+''; } return arr; };
if (standaloneMode) { var testData = [{ value: 'test1', options: [...], }, { value: 'test2', options: [...], }];
callback(format(testData)); return; }
if (this.cache) { callback(this.cache); } else { var successCallback = function(JSONString) { data = JSON.parse(JSONString); callback(format(data)); }; CordovaWrapper.getRawData(successCallback, errorCallback); } }
И использование:
DataProvider.getData(function (data) { // здесь мы используем эти полученные и отформатированные данные });
Были мысли загружать данные в провайдер до использования. Но, опять же, нет гарантий, что момент, когда эти данные будут нужны, не наступит раньше, чем эти данные будут получены из cordova.
Как правильно сделать? Нормально ли это, хотеть отдельный класс/объект, который будет предоставлять данные? Нормально ли иметь столько коллбэков из-за асинхронности? Как делать такого рода кеширование? Как не дублировать строчки вида
callback(format(data));
?


Ответ

JavaScript это ивентво ориентированный язык, то есть лапша из колбеков это обычное дело. К сожелению текущая версия JavaScript не позволяет упростить работу с колбеками, и для этого вам необходимо подобрать библиотеку для упрощения логики работы с событиями.
Замечу что если у вас в приложении действительно много колбеков то возможно стоит ознакомиться с Функциональным реактивным программированием. Вот очень мотивирующи доклад к изучению FRP.
Но в большинстве случаев FRP будет излишним, и можно воспользоваться Promise (на самом деле это частный случай FRP). Промисы будут достепны в ES6 а пока вы можете воспользоваться реализацией промисов из какой-нибудь библиотеки например jquery
Promise - это специальный объект который выпонит успешный callback или callback с ошибкой и гарантирует что один из этих колбеков будет выполнен и выполнен только один раз. На основании этих условий есть разные средства для комбинирования промисов, например метод when из jquery. Если вам нужно больше вариантов для комбинирования промисов, посмотрите библиотеку Q
По поводу вашего вопроса с кешированием ответа я приведу пример с использованием jquery, в других библотеках решение не должно сильно отличаться.
В jquery есть класс Deferred который помогает создавать промисы.
Вам нужно создать объект Deferred var deferred = new $.Deferred() например в вашем врапере и вернуть промис deferred.promise() В вашем врапере в момент получения данных необходимо вызвать метод resolve если получение данных было успешным или reject в противном случае. У обекта который вы получите в результате метода deferred.promise() будут методы done и fail которые принимают callback. Обратите внимание что колбек вызовится в любом случае, даже если получение данных произошло до добавления колбеков в методы done и fail
Таким образом вы можете создать 1 объект Deferred для каждого запроса в функции получения данных и возвращать промис каждый раз при обрашении к функции. В итоге вы получите кеширование данных и избавитесь от лапшы колбеков.
Примеры Deferred
Статья про промисы

Комментариев нет:

Отправить комментарий