I have created a shopify app that uses the graphql api and gets data.
I want to set up the fetch on another thread and want to use a web worker I have created the worker.js in the assets folder but the domain of this file is on the https://cdn.shopify.com and not https://mystore.myshopify.com/ domain and the worker wont work as I get the error "cannot be accessed from origin"
Here is the worker I am using to test getting data
var worker;
testWorker();
function testWorker()
{
if (typeof(Worker) !== "undefined")
{
if (typeof(worker) == "undefined")
{
worker = new Worker("{{ 'webworkertest.js'| asset_url }}");
}
worker.onmessage = function(event)
{
document.getElementById("loopcount").innerHTML = event.data;
}
}
else
{
document.getElementById("loopcount").innerHTML = "Web Worker not supported";
}
}
function terminateWorker()
{
worker.terminate();
worker = undefined;
}
testMainThread();
function testMainThread()
{
for (var i = 0; i < 200000; i++)
{
document.getElementById("loopcount").innerHTML = "Main Thread Counter: " + i;
}
}
And here is the worker.js file
i = 0;
while (i < 200000) {
postMessage("Web Worker Counter: " + i);
i++;
}
Related
I am working on outlook addin and have to call another funcion after collecting all to, cc, bcc
I want to process all information with to1, cc1, bcc1 but
After the toRecipients.getAsync() is called, the control returns to process function and to1 is empty.
How do I handle this ?
Also within getAllRecipients(), after the getAsync() is called, variables 'to1' / 'cc1' / 'bcc1' outside its scope (getAsync) are empty.
Any better way to handle both the above cases ?
Please see the below function and callers
function getAllRecipients(event) {
var to1 = [];
var cc1 = [];
var bcc1 = [];
// Local objects to point to recipients of either
// the appointment or message that is being composed.
// bccRecipients applies to only messages, not appointments.
var toRecipients, ccRecipients, bccRecipients;
item = Office.context.mailbox.item;
// Verify if the composed item is an appointment or message.
if (item.itemType == Office.MailboxEnums.ItemType.Appointment) {
toRecipients = item.requiredAttendees;
ccRecipients = item.optionalAttendees;
} else {
toRecipients = item.to;
ccRecipients = item.cc;
bccRecipients = item.bcc;
}
// Use asynchronous method getAsync to get each type of recipients
// of the composed item. Each time, this example passes an anonymous
// callback function that doesn't take any parameters.
if (toRecipients) {
toRecipients.getAsync(function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed){
console.log(asyncResult.error.message);
}
else {
// Async call to get to-recipients of the item completed.
// Display the email addresses of the to-recipients.
console.log('To-recipients of the item:');
for (var i = 0; i < asyncResult.value.length; i++) {
var mailID = asyncResult.value[i].emailAddress;
to1.push(mailID);
}
}
}); // End getAsync for to-recipients.
}
// Get any cc-recipients.
if (ccRecipients) {
ccRecipients.getAsync(function(asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
} else {
// Async call to get cc-recipients of the item completed.
// Display the email addresses of the cc-recipients.
console.log("Cc-recipients of the item:");
for (var i = 0; i < asyncResult.value.length; i++) {
var mailID = asyncResult.value[i].emailAddress;
cc1.push(mailID);
}
}
}); // End getAsync for cc-recipients.
}
// If the item has the bcc field, i.e., item is message,
// get any bcc-recipients.
if (bccRecipients) {
bccRecipients.getAsync(function(asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
} else {
// Async call to get bcc-recipients of the item completed.
// Display the email addresses of the bcc-recipients.
console.log("Bcc-recipients of the item:");
for (var i = 0; i < asyncResult.value.length; i++) {
var mailID = asyncResult.value[i].emailAddress;
bcc1.push(mailID);
}
}
}); // End getAsync for bcc-recipients.
}
return to1;
}
getAllRecipients(event);
process(to1);
The recipient APIs for compose are asynchronous, so you will need to await the resolved value from the API.
There is some documentation about asynchronous programming with Office add-ins and some common patterns with asynchronous functions here.
I created a script that tracks attendance for distance learning. After a while it times out so I think I am having issues with too many calls to the Google Classroom API, however I don't see a way that I can change it to take those calls out of a loop.
The script takes all the Google Classroom classes that my apps script account is a co-teacher on and using timed triggers creates a daily attendance assignment with one question that says 'here'. Students are then supposed to answer the question and then another trigger at night runs the function to 'grade' each assignment and populate my spreadsheet so school secretaries can view it in the morning and record the previous days attendance.
The part that seems to have the bottleneck is my getStudentResponses() function. I tried to reduce time by filtering out students that didn't submit the assignment, but it wasn't enough. Does anyone see any way that I can make this faster? I was reading up on using the Cache Service, but I couldn't figure out how to get that to work. Any help would be appreciated.
var ss = SpreadsheetApp.getActive();
var date = new Date();
/*
creates a button to programmatically create all necessary timed triggers for easy deployment
*/
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Attendance')
.addItem('Create Triggers', 'createTriggers')
.addToUi();
}
/*
auto accepts any co-teacher invites
*/
function acceptInvite() {
try{
var optionalArgs = {
userId: "me"
};
var invites = Classroom.Invitations.list(optionalArgs);
for(var i = 0; i < invites.invitations.length; i++) {
Classroom.Invitations.accept(invites.invitations[i].id);
}
}
catch(e){}
}
/*
populates a spreadsheet with all the classes that the script Google account is a co-teacher of
the sheet has two columns one with the course name and two with the course id
*/
function listCourses() {
var optionalArgs = {courseStates: "ACTIVE"};
var response = Classroom.Courses.list(optionalArgs);
var courses = response.courses;
var classSheet;
try{
classSheet = ss.insertSheet("Classes", 0);
ss.insertSheet("Assignments", 1);
}
catch(e) {
classSheet = ss.getSheetByName("Classes");
}
classSheet.clear();
if (courses && courses.length > 0) {
for (i = 0; i < courses.length; i++) {
var course = courses[i];
classSheet.appendRow([course.name, course.id]);
}
}
}
/*
reads the sheet to get all the classes and creates a new array with all the class IDs
*/
function getCourses() {
var classSheet = ss.getSheetByName("Classes");
var classList = new Array();
var range = classSheet.getDataRange();
var values = range.getValues();
for(var i in values) {
var row = values[i];
var courseId = row[1]+"";
classList.push(Classroom.Courses.get(courseId));
}
createTopics(classList);
}
/*
called immediatly after getCourses, creates topics in each class that will contain the daily attendance assignment
*/
function createTopics(classList) {
for(i = 0; i < classList.length; i++) {
var topic;
var resource = {name: "Daily Online Attendance"};
try {
topic = Classroom.Courses.Topics.create(resource, classList[i].id);
createAssignment(topic,classList[i]);
}
catch(e) {
if(e == "GoogleJsonResponseException: API call to classroom.courses.topics.create failed with error: Requested entity already exists") {
var topics = Classroom.Courses.Topics.list(classList[i].id);
for(j = 0; j < topics.topic.length; j++) {
if(topics.topic[j].name == "Daily Online Attendance") {
createAssignment(topics.topic[j], classList[i]);
}
}
}
}
}
}
/*
creates an assignment in each class, under each topic
each assignment only has one choice that says "here" and is going to be 'graded' each night to track attendance
*/
function createAssignment(topic,course) {
var resource = {
title: "Attendance for "+(date.getMonth()+1)+"/"+date.getDate()+"/2020",
description: "Please fill this assignment out each day for attendance",
topicId: topic.topicId,
state: "PUBLISHED",
workType: "MULTIPLE_CHOICE_QUESTION",
multipleChoiceQuestion: {
"choices": [
"Here"
]
}
};
try {
var assignment = Classroom.Courses.CourseWork.create(resource, course.id);
var sheet = ss.getSheetByName("Assignments");
sheet.appendRow([course.id,assignment.id]);
}
catch(e){}
}
/*
creates a new sheet for each day and logs each assignement
*/
function getStudentResponses() {
var assignmentSheet = ss.getSheetByName("Assignments");
var sheet2;
var response;
assignmentSheet.sort(1, true);
try{
sheet2 = ss.insertSheet("Attendance for "+(date.getMonth()+1)+"/"+date.getDate()+"/2020",(ss.getSheets().length-(ss.getSheets().length-2)));
sheet2.appendRow(["Student Last Name","Student First Name","Grade","Class Name","Assignment Answer"]);
}
catch(e) {
sheet2 = ss.getSheetByName("Attendance for "+(date.getMonth()+1)+"/"+date.getDate()+"/2020");
}
sheet2.setFrozenRows(1);
var range = assignmentSheet.getDataRange();
var values = range.getValues();
for(var i in values) {
var row = values[i];
var courseId = row[0]+"";
var courseWorkId = row[1]+"";
try {
response = Classroom.Courses.CourseWork.StudentSubmissions.list(courseId, courseWorkId);
for(var j in response.studentSubmissions) {
if(response.studentSubmissions[j].state == "TURNED_IN") {
try {
var grade;
var email = Classroom.UserProfiles.get(response.studentSubmissions[j].userId).emailAddress;
sheet2.appendRow([Classroom.UserProfiles.get(response.studentSubmissions[j].userId).name.familyName,Classroom.UserProfiles.get(response.studentSubmissions[j].userId).name.givenName,grade,Classroom.Courses.get(courseId).name,response.studentSubmissions[j].multipleChoiceSubmission.answer]);
}
catch (e) {}
}
}
}
catch(e) {}
}
}
/*
deletes all assignemnts that were created
*/
function deleteAssignments() {
var assignmentSheet = ss.getSheetByName("Assignments");
assignmentSheet.sort(1, true);
var range = assignmentSheet.getDataRange();
var values = range.getValues();
for(var i in values) {
var row = values[i];
var courseId = row[0]+"";
var courseWorkId = row[1]+"";
try {
Classroom.Courses.CourseWork.remove(courseId, courseWorkId);
}
catch(e) {}
assignmentSheet.clear();
}
}
function createTriggers() {
ScriptApp.newTrigger('getCourses')
.timeBased()
.everyDays(1)
.atHour(6)
.create();
ScriptApp.newTrigger('getStudentResponses')
.timeBased()
.everyDays(1)
.atHour(22)
.create();
ScriptApp.newTrigger('deleteAssignments')
.timeBased()
.everyDays(1)
.atHour(23)
.create();
ScriptApp.newTrigger('listCourses')
.timeBased()
.everyDays(1)
.atHour(21)
.create();
ScriptApp.newTrigger('acceptInvite')
.timeBased()
.everyDays(1)
.atHour(20)
.create();
}
appendRow is slow, you should avoid to used it inside a for loop. Instead build an array, then pass the values using a single setValues call.
Resources
Best Practices | Apps Script
Related
Google Script Performance Slow Down
Increase my script performance Google Sheets Script
Very slow execution of for...in loop
Intro
We're developing this javascript based web application that is supposed to print receipts using the epson javascript sdk.
Right now we've got this poc where multiple printers can be added to the app and where receipts can be printed per individual printer.
The problem is that the receipt will ONLY be printer from the last added printer.
Further investigating tells us that the sdk just uses the last added (connected) printer. This can be seen at the following images.
In the first image there are 2 printers setup. Notice the different ip addresses.
In the second image we log what EpsonPrinter instance is being used while printing. Notice the ip address is clearly the first printer.
In the third image we trace the network. Notice the ip address that is actually used (ignore the error).
We created our own EpsonPrinter class that can be found here or here below.
EpsonPrinter
export default class EpsonPrinter {
name = null
ipAddress = null
port = null
deviceId = null
crypto = false
buffer = false
eposdev = null
printer = null
intervalID = null
restry = 0
constructor (props) {
const {
name = 'Epson printer',
ipAddress,
port = 8008,
deviceId = 'local_printer',
crypto = false,
buffer = false
} = props
this.name = name
this.ipAddress = ipAddress
this.port = port
this.deviceId = deviceId
this.crypto = crypto
this.buffer = buffer
this.eposdev = new window.epson.ePOSDevice()
this.eposdev.onreconnecting = this.onReconnecting
this.eposdev.onreconnect = this.onReconnect
this.eposdev.ondisconnect = this.onDisconnect
this.connect()
}
onReconnecting = () => {
this.consoleLog('reconnecting')
}
onReconnect = () => {
this.consoleLog('reconnect')
}
onDisconnect = () => {
this.consoleLog('disconnect')
if (this.intervalID === null ){
this.intervalID = setInterval(() => this.reconnect(), 5000)
}
}
connect = () => {
this.consoleLog('connect')
this.eposdev.ondisconnect = null
this.eposdev.disconnect()
this.eposdev.connect(this.ipAddress, this.port, this.connectCallback)
}
reconnect = () => {
this.consoleLog('(Re)connect')
this.eposdev.connect(this.ipAddress, this.port, this.connectCallback)
}
connectCallback = (data) => {
clearInterval(this.intervalID)
this.intervalID = null
this.eposdev.ondisconnect = this.onDisconnect
if (data === 'OK' || data === 'SSL_CONNECT_OK') {
this.createDevice()
} else {
setTimeout(() => this.reconnect(), 5000)
}
}
createDevice = () => {
console.log('create device, try: ' + this.restry)
const options = {
crypto: this.crypto,
buffer: this.buffer
}
this.eposdev.createDevice(this.deviceId, this.eposdev.DEVICE_TYPE_PRINTER, options, this.createDeviceCallback)
}
createDeviceCallback = (deviceObj, code) => {
this.restry++
if (code === 'OK') {
this.printer = deviceObj
this.printer.onreceive = this.onReceive
} else if (code === 'DEVICE_IN_USE') {
if (this.restry < 5) {
setTimeout(() => this.createDevice(), 3000)
}
}
}
onReceive = (response) => {
this.consoleLog('on receive: ', response)
let message = `Print ${this.name} ${response.success ? 'success' : 'failute'}\n`
message += `Code: ${response.code}\n`
message += `Status: \n`
if (response.status === this.printer.ASB_NO_RESPONSE) { message += ' No printer response\n' }
if (response.status === this.printer.ASB_PRINT_SUCCESS) { message += ' Print complete\n' }
if (response.status === this.printer.ASB_DRAWER_KICK) { message += ' Status of the drawer kick number 3 connector pin = "H"\n' }
if (response.status === this.printer.ASB_OFF_LINE) { message += ' Offline status\n' }
if (response.status === this.printer.ASB_COVER_OPEN) { message += ' Cover is open\n' }
if (response.status === this.printer.ASB_PAPER_FEED) { message += ' Paper feed switch is feeding paper\n' }
if (response.status === this.printer.ASB_WAIT_ON_LINE) { message += ' Waiting for online recovery\n' }
if (response.status === this.printer.ASB_PANEL_SWITCH) { message += ' Panel switch is ON\n' }
if (response.status === this.printer.ASB_MECHANICAL_ERR) { message += ' Mechanical error generated\n' }
if (response.status === this.printer.ASB_AUTOCUTTER_ERR) { message += ' Auto cutter error generated\n' }
if (response.status === this.printer.ASB_UNRECOVER_ERR) { message += ' Unrecoverable error generated\n' }
if (response.status === this.printer.ASB_AUTORECOVER_ERR) { message += ' Auto recovery error generated\n' }
if (response.status === this.printer.ASB_RECEIPT_NEAR_END) { message += ' No paper in the roll paper near end detector\n' }
if (response.status === this.printer.ASB_RECEIPT_END) { message += ' No paper in the roll paper end detector\n' }
if (response.status === this.printer.ASB_SPOOLER_IS_STOPPED) { message += ' Stop the spooler\n' }
if (!response.success) {
alert(message)
// TODO: error message?
} else {
// TODO: success -> remove from queue
}
}
printReceipt = () => {
this.consoleLog(`Print receipt, `, this)
try {
if (!this.printer) {
throw `No printer created for ${this.name}`
}
this.printer.addPulse(this.printer.DRAWER_1, this.printer.PULSE_100)
this.printer.addText(`Printed from: ${this.name}\n`)
this.printer.send()
} catch (err) {
let message = `Print ${this.name} failure\n`
message += `Error: ${err}`
alert(message)
}
}
consoleLog = (...rest) => {
console.log(`${this.name}: `, ...rest)
}
}
Poc
The full working poc can be found here.
Epson javascript sdk
2.9.0
Does anyone have any experience with the epson sdk? It it supposed to be able to support multiple connections on the same time? Please let use know.
For the ones looking for a way to handle multiple printers using this SDK. We came up with the following work around:
We created a separated 'printer app' that is responsible for handling ONE printer connection and hosted it online. We then 'load' this printer app into our app that needs multiple connections using Iframes. Communication between app and printer app is done by means of window.PostMessage API to, for example, initialise the printer with the correct printer connection and providing data that has to be printed.
It takes some effort but was the most stable solution we could come up with handling multiple connections.
If anyone else comes up with a better approach please let me know!
You can checkout our printer app here for inspiration (inspect the app because it doesn't show much visiting it just like that).
For use your class EpsonPrinter, i add also myPrinters class after your class:
class myPrinters {
printers = null;
cantidad = 0;
constructor() {
console.log("Creo la coleccion de printers");
this.printers = [];
}
inicializarConeccionImpresora(idImpresora, ip, puerto, _deviceId) {
let ipAddress = ip;
let port = puerto;
let deviceId = _deviceId;
console.log("Agrego una impresora");
let myPrinter = new EpsonPrinter(ipAddress);
myPrinter.port = port;
myPrinter.deviceId = deviceId;
myPrinter.id = idImpresora;
console.log('Id impresora antes de connect es: ' + idImpresora);
myPrinter.connect();
this.printers[this.cantidad] = myPrinter;
this.cantidad ++;
}
imprimirPruebaJS(idImpresora) {
let printer = null;
let printerTemp = null
for(var i = 0; i < this.printers.length; i++) {
printerTemp = this.printers[i];
if (printerTemp.id == idImpresora) {
printer = printerTemp.printer;
}
}
if (printer == null) {
console.log("La impresora no esta iniciada en clase myPrinters");
return;
}
printer.addText('Hola mundo texto normal\n');
printer.addFeed();
printer.addCut(printer.CUT_FEED);
}
}
call myPrinters class in this way :
myEpsonPrinters = new myPrinters();
myEpsonPrinters.inicializarConeccionImpresora(1, '192.168.0.51', 8008, 'local_printer');
myEpsonPrinters.inicializarConeccionImpresora(2, '192.168.0.52', 8008, 'local_printer');
myEpsonPrinters.imprimirPruebaJS(1)
or
myEpsonPrinters.imprimirPruebaJS(2)
Test it and tell me.
Juan
Just create multiple objects for printing simple as this
this.eposdev = [];
let printersCnt = 3;
let self = this;
for(let i=1 ; i <= printersCnt ; i++){
this.eposdev[i] = new window.epson.ePOSDevice()
this.eposdev[i].onreconnecting = function (){
this.consoleLog('reConnecting')
}
this.eposdev[i].onreconnect = function (){
this.consoleLog('onReconnect')
}
this.eposdev[i].ondisconnect = function (){
this.consoleLog('onDisconnect')
}
}
function connect(printerKey) => {
this.consoleLog('connect')
this.eposdev.ondisconnect = null
this.eposdev.disconnect()
this.eposdev.connect(self.ipAddress[printerKey], self.port[printerKey], function(){
clearInterval(self.intervalID)
self.intervalID = null
self.eposdev[i].ondisconnect = self.ondisconnect
if (data === 'OK' || data === 'SSL_CONNECT_OK') {
console.log('create device, try: ' + self.restry)
const options = {
crypto: self.crypto,
buffer: self.buffer
}
self.eposdev[printerKey].createDevice(self.deviceId, self.eposdev[printerKey].DEVICE_TYPE_PRINTER, options, function(deviceObj, code){
this.restry++
if (code === 'OK') {
self.printer[printerKey] = deviceObj
self.printer.onreceive = function(){
console.log("onreceive");
}
} else if (code === 'DEVICE_IN_USE') {
if (self.restry < 5) {
setTimeout(() => self.createDevice(printerKey), 3000)
}
})
}
} else {
setTimeout(() => self.reconnect(printerKey), 5000)
}
})
}
Epson says that with version 2.12.0 you can add more than one printer.
I am developing API for to process around 25000 records. For each record I have to call another API which will return additional details for each product .These details needs to be updated in my database
The problem is since , I am processing large no of records & different API is being called inside my API, processing time is very High & data may be processed incorrectly .
[Route("GetSymbolDetailsParallel/{Exchange?}/{MarketGuid?}/{Symbol?}")]
public async Task<IActionResult> GetSymbolDetailsParallel(string Exchange = "", string MarketGuid = "", string Symbol = "")
{
GlobalFunctions objGolobal = new GlobalFunctions();
MongoClient client = new MongoClient(_ConFigSettings.MongoConnectionString);
var db = client.GetDatabase(_ConFigSettings.DatabaseName);
if (!string.IsNullOrEmpty(Symbol) && !string.IsNullOrEmpty(Exchange))
{
SymbolsBE objSymbols = db.GetCollection<SymbolsBE>("Symbols").Find(x => x.Symbol == Symbol + '.' + Exchange).FirstOrDefault();
await objGolobal.getSymbolsDetails(objSymbols, _ConFigSettings);
}
else if (!string.IsNullOrEmpty(MarketGuid))
{
try
{
// Get the List from the MongoDb Database & Pass the list to the function
List<SymbolsBE> lstSymbols = db.GetCollection<SymbolsBE>("Symbols").Find(x => x.MarketGuid == MarketGuid
&& x.isActive == true
&& (x.Fundamental == null || x.Fundamental.Code == "")).ToList();
GetMultipleFundamentalAsync(lstSymbols, _ConFigSettings);
}
catch (Exception ex)
{
}
}
return Ok("Sucess");
}
public async Task GetMultipleFundamentalAsync(List<SymbolsBE> lst, ConfigSettings _ConFigSettings)
{
DateTime StartDate = DateTime.Now;
int cnt = lst.Count;
for (int i = 0; i < cnt; i++)
{
await Task.Factory.StartNew(async () =>
{
await getSymbolsDetails(lst[i], _ConFigSettings);
});
}
}
public async Task getSymbolsDetails(SymbolsBE objSymbol, ConfigSettings _ConFigSettings)
{
// Code Download Details from API using HttpResponseMessage Res = await client.GetAsync(URL)
// This response will be saved in the Database
}
I do not know what I am talking about here I go.
On some pages it filters them and others like Youtube comments don't work.
What code needs to change in order for it to work in these sites?
// ==UserScript==
// #name profanity_filter
// #namespace localhost
// #description Profanity filter
// #include *
// #version 1
// #grant none
// ==/UserScript==
function recursiveFindTextNodes(ele) {
var result = [];
result = findTextNodes(ele,result);
return result;
}
function findTextNodes(current,result) {
for(var i = 0; i < current.childNodes.length; i++) {
var child = current.childNodes[i];
if(child.nodeType == 3) {
result.push(child);
}
else {
result = findTextNodes(child,result);
}
}
return result;
}
var l = recursiveFindTextNodes(document.body);
for(var i = 0; i < l.length; i++) {
var t = l[i].nodeValue;
t = t.replace(/badword1|badword2|badword3/gi, "****");
t = t.replace(/badword4/gi, "******");
t = t.replace(/badword5|badword6|badword7/gi, "*****");
t = t.replace(/badword8/gi, "******");
l[i].nodeValue = t;
}
* Replaced profanity in code to badword
Youtube comments are loaded asynchronously, quite a long time after the page has loaded (userscripts by default are executed at DOMContentLoaded event), so you need to wrap your code as a callback function of waitForKeyElements with a selector for the comments container or MutationObserver or setInterval.
replaceNodes(); // process the page
waitForKeyElements('.comment-text-content', replaceNodes);
function replaceNodes() {
..............
..............
}
Using setInterval instead of waitForKeyElements:
replaceNodes(); // process the page
var interval = setInterval(function() {
if (document.querySelector('.comment-text-content')) {
clearInterval(interval);
replaceNodes();
}
}, 100);
function replaceNodes() {
..............
..............
}
P.S. Don't blindly assign the value to the node, check first if it has changed to avoid layout recalculations:
if (l[i].nodeValue != t) {
l[i].nodeValue = t;
}