وسيط جافا سكريبت غير متصل

في مشروعي ، كنت بحاجة إلى وظائف تسمح لي بعدم فقدان البيانات التي تم إدخالها في حالة حدوث انقطاع في الاتصال بالإنترنت ، ولقد توصلت إلى "وسيط" بسيط للغاية يسمح لي بعدم فقدان البيانات عند فقد الاتصال ، ولكن أرسلها عند استعادة الاتصال مرة أخرى. ربما لا يكون "وسيط" اسماً جيداً بالنسبة له ، لكن لا تحكم بدقة. أريد أن أشارك ، ربما يكون شخص ما مفيدًا.

عن المشروع


تم تصميم مشروعي لاحتساب النفقات والدخل أو كإصدار بسيط من المحاسبة المنزلية. تم إنشاؤه كتطبيق ويب تقدمي ، بحيث يكون ملائمًا استخدامه على الأجهزة المحمولة ، وكذلك لفتح إمكانيات الإشعارات الفورية ، والوصول إلى الكاميرا لقراءة الباركود ، وما شابه. هناك تطبيق جوال مشابه يسمى ZenMoney ، لكنني أردت شيئًا خاصًا بي وبطريقة خاصة بي.

ظهور الأفكار


أحاول الاحتفاظ بسجل واضح للنفقات والدخل ، ولكن نظرًا لأنه غالبًا ما يتم نسيان إضافة الوظائف الضرورية ، خاصة تلك المتعلقة بالنقد ، يجب أن أقوم بذلك فورًا عند حدوث "المعاملة". في بعض الأحيان ، أدخلت البيانات في وسائل النقل العام ، مثل مترو الأنفاق ، حيث تحدث خسائر الاتصال غالبًا ، حتى على الرغم من شبكة Wi-Fi واسعة الانتشار. كان من العار أن يتجمد كل شيء ولا يحدث شيء ، ثم تُفقد البيانات ببساطة.

جاءت الفكرة من استخدام وسيط طابور مثل RabbitMQ. بالطبع ، لدي حل أبسط وليس عمليًا ، ولكن هناك شيء مشابه لمبادئه. اعتقدت أنه يمكنك حفظ كل شيء ، على سبيل المثال ، في ذاكرة التخزين المؤقت أو LocalStorage ككائن بقائمة انتظار لطلبات "غير مرضية" ، وعندما يظهر اتصال ، يمكنك تنفيذها بأمان. بالطبع ، لا يتم تنفيذها بترتيب الأولوية ، والتي ، بفضل المعالجة غير المتزامنة للطلبات بلغة JS ، هي أفضل ، نظرًا لأن لديك مشتركًا واحدًا فقط. لقد واجهت بعض الصعوبات ، ربما حتى تنفيذ كل هذا سيبدو معوجًا قليلاً ، ولكن هذا حل عملي. بالطبع ، يمكن تحسينه ، ولكن في الوقت الحالي سأصف خيار "الخام" القائم ولكن العمل.

الشروع في العمل


أول شيء فكرت فيه هو مكان تخزين البيانات في حالة عدم وجود اتصال ؟! فاكر الخدمة المفروض علي من قبل سلطة المياه الفلسطينية يعمل بشكل جيد مع ذاكرة التخزين المؤقت ، ولكن هل من الحكمة استخدام ذاكرة التخزين المؤقت ؟! سؤال صعب ، لن أخوض فيه. بشكل عام ، قررت أن LocalStorage أفضل بالنسبة لي. نظرًا لأن LocalStorage يخزن قيمًا لمفتاح type: value ، يجب إضافة الكائن كسلسلة Json. في مشروعي للتطوير الخارجي ، أضفت دليلًا يسمى QueueBroker إلى مجلد الفصل الدراسي

هيكل الملف
/**----**/
├── app.js
├── bootstrap.js
├── classes
│ └── QueueBroker
│ ├── index.js
│ └── Library
│ ├── Broker.js
│ └── Storage.js
├── components
/**----**/


تم إنشاء مشروعي في مكدس Laravel + VueJs ، لذا يلزم اعتماد معين على بنية الملف. لا أعرف كيف يصح في مثل هذه الحالات استدعاء الدلائل الخاصة بك للفصول ، لذلك فعلت ذلك.

تم إنشاء ملف الفهرس لمجرد توصيل الوحدات النمطية من المكتبة المتداخلة. قد لا يكون هذا حلًا أنيقًا للغاية ، لكنني أردت أن أقوم بذلك حتى إذا غيرت رأيي فجأة حول استخدام LocalStorage ، سأكتب فئة أخرى للتخزين بنفس الطرق ، وأمررها إلى مُنشئ الوسيط ، واستخدم التخزين الآخر دون تغيير أي شيء.

index.js
 const Broker = require('./Library/Broker'); const Storage = require('./Library/Storage'); module.exports.Broker = Broker; module.exports.Storage = Storage; 


تسمح لك هذه الطريقة بتوصيل المكتبات التي أحتاجها فقط في النصوص البرمجية ، على سبيل المثال ، إذا كنت بحاجة إلى كليهما:

 import {Storage, Broker} from '../../classes/QueueBroker/index'; 

لتسهيل تغيير فئة التخزين ، قمت بعمل نوع من المنشئات لفئة الوسيط ، حيث يمكن تمرير كائن التخزين كوسيطة ، الشيء الرئيسي هو أنه يحتوي على الوظائف الضرورية. أعلم أنه في ES6 يمكنني كتابة الفصل والمنشئ ، لكنني قررت القيام بذلك بالطريقة القديمة - النموذج الأولي. سأكتب التعليقات مباشرة عن طريق الرمز:

Broker.js
 const axios = require('axios'); //  axios /*     .    ,            front-end  */ function Broker(storage, prefix='storageKey') { this.storage = storage; this.prefix = prefix; /*     ,      .   storage   add     json */ if(this.storage.get('broker') === null) { this.broker = {}; this.storage.add('broker', this.broker) } else { //  , Storage    Json            this.broker = this.storage.getObject('broker'); } }; // ,           Broker.prototype.queueCount = function () { return Object.keys(this.broker).length; }; //  ""    Storage,    Broker.prototype.saveToStorage = function (method, url, data) { let key = this.prefix + '_' + (Object.keys(this.broker).length + 1); this.broker[key] = {method, url, data}; //            broker,        this.storage.add('broker', this.broker); }; // ,    ,    Broker.prototype.run = function () { for (let key in this.broker) { this.sendToServer(this.broker[key], key) } } /*    .        ,     method, url  data,        ,    ,    */ Broker.prototype.sendToServer = function (object, brokerKey) { axios({ method: object.method, url: object.url, data: object.data, }) .then(response => { if(response.data.status == 200) { //   ,    delete this.broker[brokerKey]; //  this.storage.add('broker', this.broker); } else { //   ;-) console.log(response.data) } }) .catch(error => { /*           ,       */ }); }; //   export module.exports = Broker; 


بعد ذلك ، تحتاج إلى كائن التخزين نفسه ، والذي سيحفظ ويسترد كل شيء بنجاح من التخزين

التخزين
 //  debug-   function Storage(debug) { if(debug === true) { this.debugMode = true; } this.storage = window.localStorage; }; // ,     Json      Storage.prototype.addObjectToStorage = function (key, object) { this.storage.setItem(key, JSON.stringify(object)); }; //    (,   ) Storage.prototype.addStringToStorage = function (key, value) { this.storage.setItem(key, value); }; //    Storage.prototype.get = function (key) { return this.storage.getItem(key); }; //    Json ,       Storage.prototype.getObject = function (key) { try { return JSON.parse(this.storage.getItem(key)); } catch (e) { this._debug(e); this._debug(key + ' = ' + this.storage.getItem(key)); return false; } }; /* ,     ,  ,        ,   Json      */ Storage.prototype.add = function (key, value) { try { if(typeof value === 'object') { this.addObjectToStorage(key, value); } else if (typeof value === 'string' || typeof value === 'number') { this.addStringToStorage(key, value); } else { //    this._debug('2 parameter does not belong to a known type') } return this.storage; } catch (e) { //    ,    ,    if (e === QUOTA_EXCEEDED_ERR) { this._debug('LocalStorage is exceeded the free space limit') } else { this._debug(e) } } }; //  Storage.prototype.clear = function () { try { this.storage.clear(); return true; } catch (e) { this._debug(e) return false; } }; //    Storage.prototype.delete = function(key) { try { this.storage.removeItem(key); return true; } catch (e) { this._debug(e) return false; } }; // ,      Storage.prototype._debug = function(error) { if(this.debugMode) { console.error(error); } return null; }; //   module.exports = Storage; 


عندما يكون كل ما سبق جاهزًا ، يمكن استخدامه وفقًا لتقديرك ، أستخدمه على النحو التالي:

استخدم في حفظ
 //   Vue (methods) /*----*/ //      Storage   sendBroker(method, url, data) { let storage = new Storage(true); let broker = new Broker(storage, 'fundsControl'); broker.saveToStorage(method, url, data); }, //     fundsSave() { let url = '/pa/funds'; let method = ''; if(this.fundsFormType === 'create') { method = 'post'; } else if(this.fundsFormType === 'update') { method = 'put'; } else if(this.fundsFormType === 'delete') { method = 'delete'; } this.$store.commit('setPreloader', true); axios({ method: method, url: url, data: this.fundsFormData, }) .then(response=> { if(response.data.status == 200) { this.fundsFormShow = false; this.getFunds(); this.$store.commit('setPreloader', false); } else { this.$store.commit('AlertError', '    '); } }) //        .catch(error => { this.$store.commit('setAlert', { type: 'warning', status: true, message: '   . ,        ,   ' } ); this.fundsFormShow = false; this.$store.commit('setPreloader', false); //   ""  this.sendBroker(method, url, this.fundsFormData); console.error(error); }); }, 


استخدم عند إعادة الاتصال
 //   Vue /*--*/ methods: { /*----*/ /*   ,    ,   ,      ,      */ brokerSendRun() { let storage = new Storage(true); let broker = new Broker(storage, 'fundsControl'); //,   -   if(broker.queueCount() > 0) { // ,    broker.run(); //   ,   this.$store.commit('setAlert', {type: 'info', status: true, message: '      -  , ,      '}); } } } /*---*/ /*     , ,   ,          ,     ,        */ mounted() { this.brokerSendRun(); } /*---*/ 


ملاحظة


يصعب عليّ التحدث عن الرمز ، لذا حاولت تقديم الرمز في الأمثلة بأكبر قدر ممكن من التعليقات التفصيلية. إذا كانت لديك أفكار لتحسين هذا الحل أو تحسين هذه المقالة ، فسوف يسعدني رؤيتها في التعليقات. أخذت أمثلة من مشروعي الخاص على Vue ، وأشرح ذلك من أجل توضيح سبب تسميتي بأساليبي ولماذا يمكنني الوصول إليها من خلال هذا . لا أفعل ذلك في هذه المقالة تحديدًا على Vue ، لذلك لا أقدم كود مكون آخر ، أتركه لفهمه.

Source: https://habr.com/ru/post/ar428980/


All Articles