utils/inject.js
function injectAsync(_function, ...args) { return new Promise((resolve, reject) => {
	if (typeof _function !== 'function') { throw new TypeError('Injecting a string is a form of eval()'); }
	const { document, } = (this || global); // call with an iframe.contentWindow as this to inject into its context

	const script = document.createElement('script');
	script.dataset.args = JSON.stringify(args);
	script.dataset.source = _function +''; // get the functions source
	script.textContent = (`(`+ function (script) {
		const args = JSON.parse(script.dataset.args);
		const _function = new Function('return ('+ script.dataset.source +').apply(this, arguments);');
		script.dataset.done = true;
		Promise.resolve().then(() => _function.apply(this, args)) // eslint-disable-line no-invalid-this
		.then(value => report('value', value))
		.catch(error => report('error', error));
		function report(type, value) {
			value = type === 'error' && (value instanceof Error)
			? '$_ERROR_$'+ JSON.stringify({ name: value.name, message: value.message, stack: value.stack, })
			: JSON.stringify(value);
			script.dispatchEvent(new this.CustomEvent(type, { detail: value, })); // eslint-disable-line no-invalid-this
		}
	} +`).call(this, document.currentScript)`);
	document.documentElement.appendChild(script).remove(); // evaluates .textContent synchronously in the page context

	if (!script.dataset.done) {
		throw new EvalError('Script was not executed at all'); // may fail due to sandboxing or CSP
	}

	function reported({ type, detail: value, }) {
		if (typeof value !== 'string') { throw new Error(`Unexpected event value type in injectAsync`); }
		switch (type) {
			case 'value': {
				resolve(JSON.parse(value));
			} break;
			case 'error': {
				reject(parseError(value));
			} break;
			default: {
				throw new Error(`Unexpected event "${ type }" in injectAsync`);
			}
		}
		script.removeEventListener('value', reported);
		script.removeEventListener('error', reported);
	}
	script.addEventListener('value', reported);
	script.addEventListener('error', reported);
}); }
function injectAsync(_function, ...args) { return new Promise((resolve, reject) => {
	if (typeof _function !== 'function') { throw new TypeError('Injecting a string is a form of eval()'); }
	const { document, } = (this || global); // call with an iframe.contentWindow as this to inject into its context

	const script = document.createElement('script');
	script.dataset.args = JSON.stringify(args);
	script.dataset.source = _function +''; // get the functions source
	script.textContent = (`(`+ function (script) {
		const args = JSON.parse(script.dataset.args);
		const _function = new Function('return ('+ script.dataset.source +').apply(this, arguments);');
		script.dataset.done = true;
		Promise.resolve().then(() => _function.apply(this, args)) // eslint-disable-line no-invalid-this
		.then(value => report('value', value))
		.catch(error => report('error', error));
		function report(type, value) {
			value = type === 'error' && (value instanceof Error)
			? '$_ERROR_$'+ JSON.stringify({ name: value.name, message: value.message, stack: value.stack, })
			: JSON.stringify(value);
			script.dispatchEvent(new this.CustomEvent(type, { detail: value, })); // eslint-disable-line no-invalid-this
		}
	} +`).call(this, document.currentScript)`);
	document.documentElement.appendChild(script).remove(); // evaluates .textContent synchronously in the page context

	if (!script.dataset.done) {
		throw new EvalError('Script was not executed at all'); // may fail due to sandboxing or CSP
	}

	function reported({ type, detail: value, }) {
		if (typeof value !== 'string') { throw new Error(`Unexpected event value type in injectAsync`); }
		switch (type) {
			case 'value': {
				resolve(JSON.parse(value));
			} break;
			case 'error': {
				reject(parseError(value));
			} break;
			default: {
				throw new Error(`Unexpected event "${ type }" in injectAsync`);
			}
		}
		script.removeEventListener('value', reported);
		script.removeEventListener('error', reported);
	}
	script.addEventListener('value', reported);
	script.addEventListener('error', reported);
}); }
function injectAsync ( _function , ... args ) { return new Promise ( ( resolve , reject ) => { if ( typeof _function !== 'function' ) { throw new TypeError ( 'Injecting a string is a form of eval()' ) ; } const { document , } = ( this || global ) ; // call with an iframe.contentWindow as this to inject into its context const script = document . createElement ( 'script' ) ; script . dataset . args = JSON . stringify ( args ) ; script . dataset . source = _function + '' ; // get the functions source script . textContent = ( ` ` + function ( script ) { const args = JSON . parse ( script . dataset . args ) ; const _function = new Function ( 'return (' + script . dataset . source + ').apply(this, arguments);' ) ; script . dataset . done = true ; Promise . resolve ( ) . then ( ( ) => _function . apply ( this , args ) ) // eslint-disable-line no-invalid-this . then ( value => report ( 'value' , value ) ) . catch ( error => report ( 'error' , error ) ) ; function report ( type , value ) { value = type === 'error' && ( value instanceof Error ) ? '$_ERROR_$' + JSON . stringify ( { name : value . name , message : value . message , stack : value . stack , } ) : JSON . stringify ( value ) ; script . dispatchEvent ( new this . CustomEvent ( type , { detail : value , } ) ) ; // eslint-disable-line no-invalid-this } } + ` ` ) ; document . documentElement . appendChild ( script ) . remove ( ) ; // evaluates .textContent synchronously in the page context if ( ! script . dataset . done ) { throw new EvalError ( 'Script was not executed at all' ) ; // may fail due to sandboxing or CSP } function reported ( { type , detail : value , } ) { if ( typeof value !== 'string' ) { throw new Error ( ` ` ) ; } switch ( type ) { case 'value' : { resolve ( JSON . parse ( value ) ) ; } break ; case 'error' : { reject ( parseError ( value ) ) ; } break ; default : { throw new Error ( ` ${ type } ` ) ; } } script . removeEventListener ( 'value' , reported ) ; script . removeEventListener ( 'error' , reported ) ; } script . addEventListener ( 'value' , reported ) ; script . addEventListener ( 'error' , reported ) ; } ) ; }
Same as `inject`, only that it executes `_function` asynchronously,
allows `_function` to return a Promise, and that it returns a Promise to that value.
The calling overhead is even greater than that of the synchronous `inject`.
@this   {Window}
Same as inject only that it executes _function asynchronously allows _function to return a Promise and that it returns a Promise to that value . The calling overhead is even greater than that of the synchronous inject .