Skip to content

Commit bd5be63

Browse files
New web worker sample (#96)
* initial draft from Shaofeng * add copyright headers * rewrites to readme with images * renaming job variables for clarity * added UI animation for reference * small code and comment cleanups * added correct button icons * added correct button icons * update image * Fixes and cleanup work to readme * update manifests * created cleaner localhost references (#97) Co-authored-by: David Chesnut <[email protected]> Co-authored-by: Maarten van Stam <[email protected]>
1 parent b627c2a commit bd5be63

16 files changed

+1065
-0
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
self.addEventListener('message',
5+
function(event) {
6+
var job = event.data;
7+
if (typeof(job) == "string") {
8+
job = JSON.parse(job);
9+
}
10+
11+
var jobId = job.jobId;
12+
try {
13+
var result = invokeFunction(job.name, job.parameters);
14+
// check whether the result is a promise.
15+
if (typeof(result) == "function" || typeof(result) == "object" && typeof(result.then) == "function") {
16+
result.then(function(realResult) {
17+
postMessage(
18+
{
19+
jobId: jobId,
20+
result: realResult
21+
}
22+
);
23+
})
24+
.catch(function(ex) {
25+
postMessage(
26+
{
27+
jobId: jobId,
28+
error: true
29+
}
30+
)
31+
});
32+
}
33+
else {
34+
postMessage({
35+
jobId: jobId,
36+
result: result
37+
});
38+
}
39+
}
40+
catch(ex) {
41+
postMessage({
42+
jobId: jobId,
43+
error: true
44+
});
45+
}
46+
}
47+
);
48+
49+
function invokeFunction(name, parameters) {
50+
if (name == "TEST") {
51+
return test.apply(null, parameters);
52+
}
53+
else if (name == "TEST_PROMISE") {
54+
return test_promise.apply(null, parameters);
55+
}
56+
else if (name == "TEST_ERROR") {
57+
return test_error.apply(null, parameters);
58+
}
59+
else if (name == "TEST_ERROR_PROMISE") {
60+
return test_error_promise.apply(null, parameters);
61+
}
62+
else {
63+
throw new Error("not supported");
64+
}
65+
}
66+
67+
function test(n) {
68+
var ret = 0;
69+
for (var i = 0; i < n; i++) {
70+
ret += Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.atan(50))))))))));
71+
for (var l = 0; l < n; l++) {
72+
ret -= Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.atan(50))))))))));
73+
}
74+
}
75+
return ret;
76+
}
77+
78+
79+
function test_promise(n) {
80+
return new Promise(function(resolve, reject) {
81+
setTimeout(function() {
82+
resolve(test(n));
83+
}, 1000);
84+
});
85+
}
86+
87+
function test_error(n) {
88+
throw new Error();
89+
}
90+
91+
function test_error_promise(n) {
92+
return Promise.reject(new Error());
93+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
var SampleNamespace = {};
5+
6+
(function(SampleNamespace) {
7+
// The max number of web workers to be created
8+
var g_maxWebWorkers = 4;
9+
10+
// The array of web workers
11+
var g_webworkers = [];
12+
13+
// Next job id
14+
var g_nextJobId = 0;
15+
16+
// The promise info for the job. It stores the {resolve: resolve, reject: reject} information for the job.
17+
var g_jobIdToPromiseInfoMap = {};
18+
19+
function getOrCreateWebWorker(jobId) {
20+
var index = jobId % g_maxWebWorkers;
21+
if (g_webworkers[index]) {
22+
return g_webworkers[index];
23+
}
24+
25+
// create a new web worker
26+
var webWorker = new Worker("functions-worker.js");
27+
webWorker.addEventListener('message', function(event) {
28+
var jobResult = event.data;
29+
if (typeof(jobResult) == "string") {
30+
jobResult = JSON.parse(jobResult);
31+
}
32+
33+
if (typeof(jobResult.jobId) == "number") {
34+
var jobId = jobResult.jobId;
35+
// get the promise info associated with the job id
36+
var promiseInfo = g_jobIdToPromiseInfoMap[jobId];
37+
if (promiseInfo) {
38+
if (jobResult.error) {
39+
// The web worker returned an error
40+
promiseInfo.reject(new Error());
41+
}
42+
else {
43+
// The web worker returned a result
44+
promiseInfo.resolve(jobResult.result);
45+
}
46+
delete g_jobIdToPromiseInfoMap[jobId];
47+
}
48+
}
49+
});
50+
51+
g_webworkers[index] = webWorker;
52+
return webWorker;
53+
}
54+
55+
// Post a job to the web worker to do the calculation
56+
function dispatchCalculationJob(functionName, parameters) {
57+
var jobId = g_nextJobId++;
58+
return new Promise(function(resolve, reject) {
59+
// store the promise information.
60+
g_jobIdToPromiseInfoMap[jobId] = {resolve: resolve, reject: reject};
61+
var worker = getOrCreateWebWorker(jobId);
62+
worker.postMessage({
63+
jobId: jobId,
64+
name: functionName,
65+
parameters: parameters
66+
});
67+
});
68+
}
69+
70+
SampleNamespace.dispatchCalculationJob = dispatchCalculationJob;
71+
})(SampleNamespace);
72+
73+
74+
CustomFunctions.associate("TEST", function(n) {
75+
return SampleNamespace.dispatchCalculationJob("TEST", [n]);
76+
});
77+
78+
CustomFunctions.associate("TEST_PROMISE", function(n) {
79+
return SampleNamespace.dispatchCalculationJob("TEST_PROMISE", [n]);
80+
});
81+
82+
CustomFunctions.associate("TEST_ERROR", function(n) {
83+
return SampleNamespace.dispatchCalculationJob("TEST_ERROR", [n]);
84+
});
85+
86+
CustomFunctions.associate("TEST_ERROR_PROMISE", function(n) {
87+
return SampleNamespace.dispatchCalculationJob("TEST_ERROR_PROMISE", [n]);
88+
});
89+
90+
91+
// This function will show what happens when calculations are run on the main UI thread.
92+
// The task pane will be blocked until this method completes.
93+
CustomFunctions.associate("TEST_UI_THREAD", function(n) {
94+
var ret = 0;
95+
for (var i = 0; i < n; i++) {
96+
ret += Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.atan(50))))))))));
97+
for (var l = 0; l < n; l++) {
98+
ret -= Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.atan(50))))))))));
99+
}
100+
}
101+
return ret;
102+
});
103+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"functions": [
3+
{
4+
"id": "TEST_UI_THREAD",
5+
"parameters": [
6+
{
7+
"name": "n"
8+
}
9+
]
10+
},
11+
{
12+
"id": "TEST",
13+
"parameters": [
14+
{
15+
"name": "n"
16+
}
17+
]
18+
},
19+
{
20+
"id": "TEST_PROMISE",
21+
"parameters": [
22+
{
23+
"name": "n"
24+
}
25+
]
26+
},
27+
{
28+
"id": "TEST_ERROR",
29+
"parameters": [
30+
{
31+
"name": "n"
32+
}
33+
]
34+
},
35+
{
36+
"id": "TEST_ERROR_PROMISE",
37+
"parameters": [
38+
{
39+
"name": "n"
40+
}
41+
]
42+
}
43+
]
44+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<!-- Copyright (c) Microsoft Corporation.
2+
Licensed under the MIT License. -->
3+
4+
<html>
5+
<head>
6+
<title>Custom functions using WebWorker</title>
7+
<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript"></script>
8+
<script src="functions.js" type="text/javascript"></script>
9+
<script type="text/javascript">
10+
11+
var ballX = 100;
12+
var ballY = 10;
13+
var ballDirection = 'downRight';
14+
15+
Office.onReady(function() {
16+
animate();
17+
console.log("Office.onReady");
18+
});
19+
20+
function animate() {
21+
22+
setInterval(drawBall, 10);
23+
}
24+
25+
var drawBall = () => {
26+
var canvas = document.getElementById('mycanvas');
27+
28+
if (canvas.getContext) {
29+
var ctx = canvas.getContext('2d');
30+
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
31+
moveBall(ctx.canvas.width,ctx.canvas.height);
32+
var radius=20;
33+
34+
ctx.beginPath();
35+
ctx.arc(ballX,ballY,radius,0,2*Math.PI,false);
36+
ctx.fillStyle = 'green';
37+
ctx.fill();
38+
ctx.lineWidth = 4;
39+
ctx.strokeStyle = '#003300';
40+
ctx.stroke();
41+
}
42+
}
43+
44+
var moveBall = (width,height) => {
45+
//check for ball collision with context boundaries
46+
47+
if (ballX <= 0) {
48+
if (ballDirection === 'upLeft') {
49+
ballDirection = 'upRight';
50+
} else {
51+
ballDirection = "downRight";
52+
}
53+
}
54+
if (ballY <=0) {
55+
if (ballDirection === 'upLeft') {
56+
ballDirection = 'downLeft';
57+
58+
} else {
59+
ballDirection = "downRight";
60+
}
61+
}
62+
if (ballX >= width) {
63+
if (ballDirection ==='upRight'){
64+
ballDirection = 'upLeft';
65+
66+
} else {
67+
ballDirection = 'downLeft';
68+
}
69+
}
70+
if (ballY >= height) {
71+
if (ballDirection ==='downRight'){
72+
ballDirection = 'upRight';
73+
} else {
74+
ballDirection = 'upLeft';
75+
}
76+
}
77+
switch (ballDirection) {
78+
case 'upRight': {
79+
ballX++;
80+
ballY--;
81+
break;
82+
}
83+
case 'upLeft': {
84+
ballX--;
85+
ballY--;
86+
break;
87+
}
88+
case 'downRight': {
89+
ballX++;
90+
ballY++;
91+
break;
92+
}
93+
case 'downLeft': {
94+
ballX--;
95+
ballY++;
96+
break;
97+
}
98+
}
99+
}
100+
101+
</script>
102+
</head>
103+
<body>
104+
<canvas id = "mycanvas" width = "200" height = "200"></canvas>
105+
</body>
106+
</html>
269 Bytes
Loading
484 Bytes
Loading
1018 Bytes
Loading
1.56 KB
Loading
2.33 KB
Loading
4.72 KB
Loading

0 commit comments

Comments
 (0)