Skip to content

Instantly share code, notes, and snippets.

@jiju-MS
Created November 15, 2022 10:12
Show Gist options
  • Save jiju-MS/7575d11e1b1781133bfa9768b39ce10a to your computer and use it in GitHub Desktop.
Save jiju-MS/7575d11e1b1781133bfa9768b39ce10a to your computer and use it in GitHub Desktop.
A streaming function that continuously increments the cell value.
name: Streaming function
description: A streaming function that continuously increments the cell value.
host: EXCEL
api_set: {}
script:
content: >
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* Returns the arguments as an entity type
* @CustomFunction
* @param first First number
* @param second Second number
* @returns An entity containing the two numbers as properties
*/
function entityNoStream(first: number, second: number) {
return _pushOperation("entityNoStream", [first, second]);
}
/**
* Returns the arguments as an entity type via a streaming function (with an extra delay)
* @CustomFunction
* @streaming
* @param first First number
* @param second Second number
* @param {CustomFunctions.StreamingInvocation<any[][]>} invocation Invocation object.
* @returns An entity containing the two numbers as properties
*/
function entityAsStream(first: number, second: number, invocation:
CustomFunctions.StreamingInvocation<any[][]>) {
return _pushOperation("entityAsStream", [first, second, invocation]);
}
/**
* Defines the implementation of the custom functions
* for the function id defined in the metadata file (functions.json).
*/
CustomFunctions.associate("entityNoStream", entityNoStream);
CustomFunctions.associate("entityAsStream", entityAsStream);
///////////////////////////////////////
let _batch = [];
let _isBatchedRequestScheduled = false;
// This function encloses your custom functions as individual entries,
// which have some additional properties so you can keep track of whether or
not
// a request has been resolved or rejected.
function _pushOperation(op, args) {
// Create an entry for your custom function.
console.log("pushOperation");
const invocationEntry = {
operation: op, // e.g. sum
args: args,
resolve: undefined,
reject: undefined
};
// Create a unique promise for this invocation,
// and save its resolve and reject functions into the invocation entry.
const promise = new Promise((resolve, reject) => {
invocationEntry.resolve = resolve;
invocationEntry.reject = reject;
});
// Push the invocation entry into the next batch.
_batch.push(invocationEntry);
// If a remote request hasn't been scheduled yet,
// schedule it after a certain timeout, e.g. 100 ms.
if (!_isBatchedRequestScheduled) {
console.log("schedule remote request");
_isBatchedRequestScheduled = true;
setTimeout(_makeRemoteRequest, 100);
}
// Return the promise for this invocation.
return promise;
}
// This is a private helper function, used only within your custom function
add-in.
// You wouldn't call _makeRemoteRequest in Excel, for example.
// This function makes a request for remote processing of the whole batch,
// and matches the response batch to the request batch.
function _makeRemoteRequest() {
// Copy the shared batch and allow the building of a new batch while you are waiting for a response.
// Note the use of "splice" rather than "slice", which will modify the original _batch array
// to empty it out.
try {
console.log("makeRemoteRequest");
const batchCopy = _batch.splice(0, _batch.length);
_isBatchedRequestScheduled = false;
// Build a simpler request batch that only contains the arguments for each invocation.
const requestBatch = batchCopy.map((item) => {
return { operation: item.operation, args: item.args };
});
console.log("makeRemoteRequest2");
// Make the remote request.
_fetchFromRemoteService(requestBatch).then((responseBatch) => {
console.log("responseBatch in fetchFromRemoteService");
// Match each value from the response batch to its corresponding invocation entry from the request batch,
// and resolve the invocation promise with its corresponding response value.
responseBatch.forEach((response, index) => {
if (response.error) {
batchCopy[index].reject(new Error(response.error));
console.log("rejecting promise");
} else {
console.log("fulfilling promise");
console.log(response);
batchCopy[index].resolve(response.result);
}
});
});
console.log("makeRemoteRequest3");
} catch (error) {
console.log("error name:" + error.name);
console.log("error message:" + error.message);
console.log(error);
}
}
// --------------------- A public API ------------------------------
// This function simulates the work of a remote service. Because each
service
// differs, you will need to modify this function appropriately to work with
the service you are using.
// This function takes a batch of argument sets and returns a [promise of]
batch of values.
// NOTE: When implementing this function on a server, also apply an
appropriate authentication mechanism
// to ensure only the correct callers can access it.
async function _fetchFromRemoteService(requestBatch) {
// Simulate a slow network request to the server;
console.log("_fetchFromRemoteService");
await pause(1000);
console.log("postpause");
return requestBatch.map((request) => {
console.log("requestBatch server side");
const { operation, args } = request;
try {
if (operation === "div2") {
// Divide the first argument by the second argument.
return {
result: args[0] / args[1]
};
} else if (operation === "mul2") {
// Multiply the arguments for the given entry.
const myResult = args[0] * args[1];
console.log(myResult);
return {
result: myResult
};
} else if (operation === "entityNoStream") {
return {
result: [[createEntity(args[0], args[1])]]
};
} else if (operation === "entityAsStream") {
setTimeout(() => {
let result = [[createEntity(args[0], args[1])]];
console.log("INVOCATION SET RESULT >> ", args[2], result);
// execute the invocation
args[2].setResult(result);
}, 1000);
return {
result: "#PENDING!"
};
} else {
return {
error: `Operation not supported: ${operation}`
};
}
} catch (error) {
return {
error: `Operation failed: ${operation}`
};
}
});
}
function pause(ms) {
console.log("pause");
return new Promise((resolve) => setTimeout(resolve, ms));
}
function createEntity(arg0: any, arg1: any): Excel.EntityCellValue {
const entity: Excel.EntityCellValue = {
type: "Entity",
text: "Numbers",
properties: {
arg0: {
type: "Double",
basicValue: arg0
},
arg1: {
type: "Double",
basicValue: arg1
}
}
};
return entity;
}
language: typescript
libraries: |
https://appsforoffice.microsoft.com/lib/1/hosted/office.js
@types/office-js
core-js@2.4.1/client/core.min.js
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment