Asynchronous read and write migration - sizzlemctwizzle/GM_config GitHub Wiki
This page covers the general and full impact on users of GM_config due to the switch to asynchronous reading and writing of stored values. This is a direct result of supporting the Greasemonkey 4+ APIs. Why did your script seem to be working just fine in Greasemonkey? GM_config had been falling back to localStorage in Greasemonkey, so it wouldn't seem like there was an impact.
Firstly, remember to use the proper @grant lines in the your script metadata:
// ==UserScript==
// @name Script Name
// @namespace Script Namespace
// @require https://openuserjs.org/src/libs/sizzle/GM_config.js
//
// Legacy GM3 support
// @grant GM_getValue
// @grant GM_setValue
// Current GM4 support
// @grant GM.getValue
// @grant GM.setValue
// ==/UserScript==Note: Code examples use initialization via the constructor, but GM_config.init is still fully supported.
Initialization is now done asynchronously, which means this will no longer work:
let gmc = new GM_config(
{
'id': 'ThisConfig',
'fields': {
'val': {
'label': 'Value',
'type': 'text'
}
}
});
// initialization is not complete
// value is not yet available
let val = gmc.get('val');Now get needs to be moved into the init event callback function:
let gmc = new GM_config(
{
'id': 'ThisConfig',
'fields': {
'val': {
'label': 'Value',
'type': 'text'
}
},
'events': {
'init': () => {
// initialization complete
// value is now available
let val = gmc.get('val');
}
}
});Another way to write this is:
let gmc = new GM_config(
{
'id': 'ThisConfig',
'fields': {
'val': {
'label': 'Value',
'type': 'text'
}
},
'events': {
'init': onInit
}
});
function onInit() {
// initialization complete
// value is now available
let val = gmc.get('val');
}Extra Example: break up get calls
// Register multiple callbacks as a single callback function
function Callbacks() {
let hasRun = false;
let runables = [];
let next = () => runables.shift();
let run = function () {
hasRun = true;
for (let fn = next(); !!fn; fn = next())
setTimeout(fn);
};
run.add = fn => {
runables.push(fn);
if (hasRun) run();
};
return run;
}
// Get a single callback function
let onInit = new Callbacks();
let gmc = new GM_config(
{
'id': 'ThisConfig',
'fields': {
'val_1': {
'label': 'Value 1',
'type': 'text'
},
'val_2': {
'label': 'Value 2',
'type': 'text'
}
},
'events': {
'init': onInit
}
});
// Break-up get() calls
onInit.add(() => {
// initialization complete
// value is now available
let val = gmc.get('val_1');
});
onInit.add(() => {
// initialization complete
// value is now available
let val = gmc.get('val_2');
});This section covers impact on advance usage. Most users can just skip this.
Potential impact on advance usage:
-
getValue,setValue,log,read,write,save, andinitare all asynchronous now (openwas already asynchronous).-
setValue,log,write, andsavecan be used like before in most cases. -
getValue,read, andinitare impacted the most. - I consider
getValue,setValue,log,read, andwriteto be internal so I don't count them as a breaking change.
-
-
saveandinitare the official API functions that are affected by a breaking change, even thoughsavewon't be impacted in most usages. - Although not asynchronous,
getfails when used beforeinithas finished.
The async execution paths are: init -> read -> getValue and save -> write -> setValue. Both getValue and setValue use a Promise, whereas read and write use callback functions (last argument).
Note: init refers to initialization that can be reach via: GM_config.init, GM_config, new GM_config, GM_configStruct, new GM_configStruct , configInstance.init