fastest way sending datas from Injected to content Script - javascript

I am building a chrome extension and getting datas from a webpage(a list with 20 elements and hundreds of pages ) through my injected script.
This injected script is sending the datas via chrome storage to the background script.
The background script is calculating an array.
Then sending the result to my content script where a mutation observer is waiting for an event where it’s using the calculated array.
I am sending all these data’s around by chrome.local.storage.set / get.
Because so there are so many different specs around, my mutation observer has an timeout of 1second for every loaded page / mutation because else the data’s are loaded to slow and it still has the data’s from the page before.
Is there a faster way sending these data’s around besides the chrome storage ?
Injected.js
//Gettig Datas before as constant players
const payload = {
PlayerList: players.map(player => {
return {
ID: player.id, //resource Id
Price: player.bin // Price
};
}),
};
var payload2 = Object.values(payload);
chrome.runtime.sendMessage(extId, {type: 'GetPlayerList', data: payload2});
background.js
chrome.runtime.onMessageExternal.addListener(
function (request, sender, sendResponse) {
if (request.type === "GetPlayerList") {
var playertest = request;
var playertest2 = playertest.data[0];
var playerbase = chrome.storage.local.get("datafut", function (data) {
playerbase = data.datafut;
var data = mergeArrays(playertest2, playerbase);
chrome.storage.local.set(
{
playerDataListCurrent: data
});
console.log(mergeArrays(playertest2, playerbase));
})
}
});
function mergeArrays(playertest2, playerbase) { //Calculate an array
by filter the IDs from a 30Element Array and a 500Element Array}
mergeArrays function: array
content.js
var s = document.createElement('script');
s.src = chrome.extension.getURL('injected.js');
s.dataset.variable = JSON.stringify(chrome.runtime.id);
s.asnyc = false; (document.head ||
document.documentElement).appendChild(s); s.onload = function () { s.remove(); };
var observeTransferList = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
mutation.addedNodes.forEach(function (node) {
if (node.nodeType === 1 && node.matches(".has-auction-data")) {
$(node).css("height", "28");
setTimeout(() => {
var playerDataListCurrent;
chrome.storage.sync.get(function (items) {
platform = items.platform;
discountprice = items.discountprice;
average = parseInt(items.average);
percentage = parseInt(items.percentage);
if (percentage === 0) {
//Data get
chrome.storage.local.get(function (items) {
playerDataListCurrent = items.playerDataListCurrent;
for (i = 0; i < playerDataListCurrent.length; i++) {
//DO STUFF
}
})
}, 1000); // Timeout for unknown delay. Else its sometimes getting datas from array calculated before
}
});
});
});

Related

How can I get a variable from a JavaScript promises (python calls), avoiding the pending state in Odoo?

Original code from the Point of Sale module
In the point_of_sale module there is a list of objects as the following
module.PosModel = Backbone.Model.extend({
models: {
// [...]
{
model: 'pos.session',
fields: ['id', 'journal_ids','name','user_id','config_id','start_at','stop_at','sequence_number','login_number'],
domain: function(self){ return [['state','=','opened'],['user_id','=',self.session.uid]]; },
loaded: function(self,pos_sessions){
self.pos_session = pos_sessions[0];
var orders = self.db.get_orders();
for (var i = 0; i < orders.length; i++) {
self.pos_session.sequence_number = Math.max(self.pos_session.sequence_number, orders[i].data.sequence_number+1);
}
},
},
{
model: 'product.product',
fields: ['display_name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'default_code',
'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description',
'product_tmpl_id'],
domain: [['sale_ok','=',true],['available_in_pos','=',true]],
context: function(self){ return { pricelist: self.pricelist.id, display_default_code: false }; },
loaded: function(self, products){
self.db.add_products(products);
},
// [...]
}
And then the information of the data is loaded like this
load_server_data: function(){
var self = this;
var loaded = new $.Deferred();
var progress = 0;
var progress_step = 1.0 / self.models.length;
var tmp = {}; // this is used to share a temporary state between models loaders
function load_model(index){
if(index >= self.models.length){
loaded.resolve();
}else{
var model = self.models[index];
self.pos_widget.loading_message(_t('Loading')+' '+(model.label || model.model || ''), progress);
var fields = typeof model.fields === 'function' ? model.fields(self,tmp) : model.fields;
var domain = typeof model.domain === 'function' ? model.domain(self,tmp) : model.domain;
var context = typeof model.context === 'function' ? model.context(self,tmp) : model.context;
var ids = typeof model.ids === 'function' ? model.ids(self,tmp) : model.ids;
progress += progress_step;
if( model.model ){
if (model.ids) {
var records = new instance.web.Model(model.model).call('read',[ids,fields],context);
} else {
var records = new instance.web.Model(model.model).query(fields).filter(domain).context(context).all()
}
// [...]
What I have tried. First try
So, I would like to change the domain field of the product.product model. I am trying with this
if (typeof jQuery === 'undefined') { throw new Error('Product multi POS needs jQuery'); }
+function ($) {
'use strict';
openerp.pos_product_multi_shop = function(instance, module) {
var PosModelParent = instance.point_of_sale.PosModel;
instance.point_of_sale.PosModel = instance.point_of_sale.PosModel.extend({
load_server_data: function(){
console.log('-- LOAD SERVER DATA');
var self = this;
self.models.forEach(function(elem) {
if (elem.model == 'product.product') {
// return [['id', 'in', [2]]]; // if I return this domain it works well
domain_loaded = function() {
return new instance.web.Model('product.product').call(
'get_available_in_pos_ids',
[self.pos_session.config_id[0]],
)
}
elem.domain = $.when(domain_loaded);
}
})
var loaded = PosModelParent.prototype.load_server_data.apply(this, arguments);
return loaded;
},
});
}
}(jQuery);
If I return a domain directly it works. But if I replace it with a function that calls a python function with call, the domain is not loaded well: [['sale_ok','=',true],['available_in_pos','=',true]]. I've tried with $.when and without it and it does not work.
In addition elem.domain must be a function because self.pos_session only exists when all the previous model information is executed.
Second try
I have tried this following code as well:
if (elem.model == 'product.product') {
// return [['id', 'in', [2]]]; // if I return the domain like this it works
console.log('>> OLD DOMAIN')
console.log(elem.domain);
elem.domain = function() {
console.log('>>> PRODUCT SESSION');
console.log(self.pos_session);
var product_product_obj = new instance.web.Model('product.product');
return product_product_obj.call(
'get_available_in_pos_ids',
[self.pos_session.config_id[0]],
)
}
console.log('>> NEW DOMAIN')
console.log(elem.domain);
}
So first '>> OLD DOMAIN' is printed, then '>> NEW DOMAIN' and, at last '>>> PRODUCT SESSION' is printed. So the function is executed. But the the domains is not being returned well.
Third try. With "then"
And I cannot use then because I need to do the variable assignation. But on the other hand the assignation is well done becase when I print the new domain the function appears in the log.
Even if I use then I am getting the result well from python
var domain_return = product_product_obj.call(
'get_available_in_pos_ids',
[self.pos_session.config_id[0]],
).then(function(result) {
console.log('>> RESULT: ');
console.log(result)
});
I also tried with other promise, but I get a pending result that is ignored and all the products are shown
elem.domain = function() {
return new Promise(function next(resolve, reject) {
console.log('>>> PRODUCT SESSION');
console.log(self.pos_session);
var product_product_obj = new instance.web.Model('product.product');
var domain_return = product_product_obj.call(
'get_available_in_pos_ids',
[self.pos_session.config_id[0]],
).then(function(result) {
console.log('>> RETURN: ');
console.log(result);
resolve(result);
});
console.log('>> DOMAIN RETURN: ');
console.log(domain_return);
});
}
The rest of the domains of the object are calculated without calling python functions. So I can't copy an example from other place
So, is there a way to avoid the pending result? I cannot use async/await yet.
Maybe to make it syncronous will help but I know this should be avoided
Finally I found a workaround overriding the loaded function where all the products are already loaded
var PosModelParent = instance.point_of_sale.PosModel;
instance.point_of_sale.PosModel = instance.point_of_sale.PosModel.extend({
load_server_data: function(){
let self = this;
self.models.forEach(function(elem) {
if (elem.model == 'product.product') {
elem.fields = ['display_name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'default_code',
'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description',
'product_tmpl_id', 'available_in_pos_ids'];
elem.loaded = function(self, products){
console.log('>> PRODUCTS: ');
console.log(products);
var shop_id = self.pos_session.config_id[0];
var new_products = [];
products.forEach(function(prod) {
if (prod.available_in_pos_ids.includes(shop_id)) {
new_products.push(prod);
}
})
self.db.add_products(new_products);
}
}
})
var loaded = PosModelParent.prototype.load_server_data.apply(this, arguments);
return loaded;
},
});

Javascript listening for data changes & trigger reload

I have a server sent event in PHP that displays inventory information client side and updates in real time. That part works great. The challenge is that I want to trigger a reload when the inventory = 0. Note: I'm using jQuery Mobile 1.4.5
Here's my code:
var source = new EventSource('listener.php');
source.onmessage = function(msg) {
document.getElementById('inv').innerHTML = msg.data;
};
source.addEventListener('msg', onMessageHandler,false);
function onMessageHandler(msg) {
var inventory = msg.data;
};
// Trying to Fire Something like this //
//if (inventory = 0) {
//setTimeout(function() {
//location.reload(true)
//},5000);
Thanks for any suggestions : )
Watch the scoping:
var source = new EventSource('listener.php');
source.onmessage = function(msg) {
document.getElementById('inv').innerHTML = msg.data;
};
source.addEventListener('msg', onMessageHandler,false);
function onMessageHandler(msg) {
var inventory = msg.data;
if (inventory === 0) {
setTimeout(function() {
location.reload(true);
}, 5000);
}
};
Thank you for the suggestions. Both helped tremendously. This is what worked for me in the event any others ever have a similar challenge:
var source = new EventSource('listener.php');
source.onmessage = function(msg) {
document.getElementById('inv').innerHTML = msg.data;
console.log(msg.data);
var x = msg.data;
if (x == 0) {
setTimeout(function() {
window.location.reload(true);
// Or any of the 1000's of other ways to reload
}, 1000);
}
};
source.addEventListener('msg', onMessageHandler,false);
function onMessageHandler(msg) {
var inventory = msg.data;
};

How to implement Infinite Scrolling with the new Firebase (2016)?

QUESTION:
How to implement efficient infinite scrolling in Firebase using javascript (and node.js) ?
WHAT I CHECKED:
Implementing Infinite Scrolling with Firebase?
Problem: older firebase ^
Infinite scroll with AngularJs and Firebase
CODE FROM:
Infinite scroll with AngularJs and Firebase
"First, I recommend to create an Index in your Firebase. For this answer, I create this one:
{
"rules": {
".read": true,
".write": false,
"messages": {
".indexOn": "id"
}
}
}
Then, let's make some magic with Firebase:
// #fb: your Firebase.
// #data: messages, users, products... the dataset you want to do something with.
// #_start: min ID where you want to start fetching your data.
// #_end: max ID where you want to start fetching your data.
// #_n: Step size. In other words, how much data you want to fetch from Firebase.
var fb = new Firebase('https://<YOUR-FIREBASE-APP>.firebaseio.com/');
var data = [];
var _start = 0;
var _end = 9;
var _n = 10;
var getDataset = function() {
fb.orderByChild('id').startAt(_start).endAt(_end).limitToLast(_n).on("child_added", function(dataSnapshot) {
data.push(dataSnapshot.val());
});
_start = _start + _n;
_end = _end + _n;
}
Finally, a better Infinite Scrolling (without jQuery):
window.addEventListener('scroll', function() {
if (window.scrollY === document.body.scrollHeight - window.innerHeight) {
getDataset();
}
});
I'm using this approach with React and it's blazing fast no matter how big your data is."
(answered Oct 26 '15 at 15:02)
(by Jobsamuel)
PROBLEM
In that solution, n posts will be loaded each time the scroll reaches the end of the height of screen.
Depending on screen sizes, this means a lot more posts than needed will be loaded at some point (the screen height only contains 2 posts, which means 3 more posts than necessary will be loaded each time we reach the end of the screen height when n = 5 for example).
Which means 3*NumberOfTimesScrollHeightHasBeenPassed more posts than needed will be loaded each time we reach the end of the scrollheight.
MY CURRENT CODE (loads all posts at once, no infinite scrolling):
var express = require("express");
var router = express.Router();
var firebase = require("firebase");
router.get('/index', function(req, res, next) {
var pageRef = firebase.database().ref("posts/page");
pageRef.once('value', function(snapshot){
var page = [];
global.page_name = "page";
snapshot.forEach(function(childSnapshot){
var key = childSnapshot.key;
var childData = childSnapshot.val();
page.push({
id: key,
title: childData.title,
image: childData.image
});
});
res.render('page/index',{page: page});
});
});
Here is full code for infinite paging.
The function createPromiseCallback is for supporting both promises and callbacks.
function createPromiseCallback() {
var cb;
var promise = new Promise(function (resolve, reject) {
cb = function (err, data) {
if (err) return reject(err);
return resolve(data);
};
});
cb.promise = promise;
return cb;
}
The function getPaginatedFeed implements actual paging
function getPaginatedFeed(endpoint, pageSize, earliestEntryId, cb) {
cb = cb || createPromiseCallback();
var ref = database.ref(endpoint);
if (earliestEntryId) {
ref = ref.orderByKey().endAt(earliestEntryId);
}
ref.limitToLast(pageSize + 1).once('value').then(data => {
var entries = data.val() || {};
var nextPage = null;
const entryIds = Object.keys(entries);
if (entryIds.length > pageSize) {
delete entries[entryIds[0]];
const nextPageStartingId = entryIds.shift();
nextPage = function (cb) {
return getPaginatedFeed(endpoint, pageSize, nextPageStartingId, cb);
};
}
var res = { entries: entries, nextPage: nextPage };
cb(null, res);
});
return cb.promise;
}
And here is how to use getPaginatedFeed function
var endpoint = '/posts';
var pageSize = 2;
var nextPage = null;
var dataChunk = null;
getPaginatedFeed(endpoint, pageSize).then(function (data) {
dataChunk = data.entries;
nextPage = data.nextPage;
//if nexPage is null means there are no more pages left
if (!nextPage) return;
//Getting second page
nextPage().then(function (secondpageData) {
dataChunk = data.entries;
nextPage = data.nextPage;
//Getting third page
if (!nextPage) return;
nextPage().then(function (secondpageData) {
dataChunk = data.entries;
nextPage = data.nextPage;
//You can call as long as your nextPage is not null, which means as long as you have data left
});
});
});
What about the question how many items to put on the screen, you can give a solution like this, for each post give fixed x height and if it requires more space put "read more" link on the bottom of the post which will reveal missing part when user clicks. In that case you can keep fixed count of items on your screen. Additionally you can check screen resolution in order to put more or less items.

Javascript Promise().then to prevent re-calling the function before the first call be executed

In my node.js app, reading data from MSSQL using tedious, I'm calling the below every 1 second:
Fetch the data from the server (fetchStock function) and save it in temporary array
Send the data saved in the temporary array to the client using the Server-Sent Events (SSE) API.
It looks the 1 second is not enough to recall the fetchStock function before the previous call is completely executed, so I get execution errors from time to time.
I increased it to 5 seconds, but still get the same issue every once in a while.
How can I use Promise().then to be sure the fetchStock function is not re-called before the previouse call be completely executed?
var Request = require('tedious').Request;
var Connection = require('tedious').Connection;
var config = {
userName: 'sa',
password: 'pswd',
server: 'xx.xxx.xx.xxx',
options: {
database: 'DB',
rowCollectionOnRequestCompletion: 'true',
rowCollectionOnDone: 'true'
},
};
var sql = new Connection(config);
var addElem = (obj, elem)=> [].push.call(obj, elem);
var result = {}, tmpCol = {}, tmpRow = {};
module.exports = {
displayStock: function (es) {
var dloop = setInterval(function() {
if(result.error !== null)
if (es) es.send(JSON.stringify(result), {event: 'rmSoH', id: (new Date()).toLocaleTimeString()});
if(result.error === null)
if (es) es.send('connection is closed');
}, 1000);
},
fetchStock: function () {
request = new Request("SELECT ItemCode, WhsCode, OnHand FROM OITW where OnHand > 0 and (WhsCode ='RM' or WhsCode ='FG');", function(err, rowCount, rows) {
if (err) {
result = {'error': err};
console.log((new Date()).toLocaleTimeString()+' err : '+err);
}
if(rows)
rows.forEach(function(row){
row.forEach(function(column){
var colName = column.metadata.colName;
var value = column.value;
addElem(tmpCol, {colName: value})
});
addElem(tmpRow,{'item': tmpCol[0].colName, 'Whs': tmpCol[1].colName, 'Qty': tmpCol[2].colName});
tmpCol = {};
});
result = tmpRow;
tmpRow={}
});
sql.execSql(request);
}
}
I think what you need is a simple variable to check if there's already running request not Promise.
var latch = false;
// It will be called only if the previous call is completed
var doFetchStock = () => sql.execSql(new Request("SQL", (err, rowCount, rows) => {
// Your logic dealing with result
// Initializes the latch
latch = false;
});
module.exports = {
fetchStock: function () {
// Check if the previous request is completed or not
if (!latch) {
// Sets the latch
latch = true;
// Fetches stock
doFetchStock();
}
}
};
Actually I've used this kind of pattern a lot to allow some behavior only once.
https://github.com/cettia/cettia-javascript-client/blob/1.0.0-Beta1/cettia.js#L397-L413
https://github.com/cettia/cettia-javascript-client/blob/1.0.0-Beta1/cettia.js#L775-L797
Since javascript is mono-threaded a simple code like this should be enough on client-side
function () {
if(currentPromise != null){ // define in a closure outside
currentPromise = [..] // call to server which return a promise
currentPromise.then(function(){
currentPromise = null;
});
}
}

Improving performance refreshing items every second

I have an API that send updates via Server Sent Events (SSE) every seconds for my items.
Basically I have a collection $scope.items that contain a lot of information within and every second one item of this list is updated.
What I'm doing is:
var source;
if (!!window.EventSource) {
source = new EventSource('/updates');
} else {
alertify.error('SSE not supported');
}
// Emit SSE for items
source.addEventListener('items', function (e) {
var data = JSON.parse(e.data);
$timeout(function () {
var item_index = _.findIndex($scope.items, function (item) {
return item.id === data.id;
});
var status = data.status;
if (item_index > -1) {
if (status === 'cancelled') {
$scope.items.splice(item_index, 1);
}
$scope.items[item_index] = data;
$scope.$apply();
} else {
$scope.items.push(data);
}
});
}, false);
I was wondering if I'm doing it right or if I can improve this code because the app is quite slow when I start to have many and many items to cycle every second...
Looking at your code:
var item_index = _.findIndex($scope.items, function (item) {
return item.id === data.id;
});
I fear that a full search is done every time you access item_index
I would define a function:
function getIndex(data){
_.findIndex($scope.items, function (item) {
return item.id === data.id;
});
};
And the call it from within your
$timeout(function () {
var item_index = getIndex(data);
...

Categories