{"id":21900089,"url":"https://github.com/keithhenry/chromeextensionasync","last_synced_at":"2025-04-06T01:06:43.743Z","repository":{"id":52290926,"uuid":"84563025","full_name":"KeithHenry/chromeExtensionAsync","owner":"KeithHenry","description":"Promise wrapper for the Chrome extension API so that it can be used with async/await rather than callbacks","archived":false,"fork":false,"pushed_at":"2021-05-01T10:21:23.000Z","size":413,"stargazers_count":227,"open_issues_count":8,"forks_count":32,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-27T14:55:21.126Z","etag":null,"topics":["async-await","chrome","chrome-extension"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/KeithHenry.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-10T13:33:35.000Z","updated_at":"2024-11-07T05:51:45.000Z","dependencies_parsed_at":"2022-08-30T14:51:35.861Z","dependency_job_id":null,"html_url":"https://github.com/KeithHenry/chromeExtensionAsync","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KeithHenry%2FchromeExtensionAsync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KeithHenry%2FchromeExtensionAsync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KeithHenry%2FchromeExtensionAsync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KeithHenry%2FchromeExtensionAsync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KeithHenry","download_url":"https://codeload.github.com/KeithHenry/chromeExtensionAsync/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247419859,"owners_count":20936012,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async-await","chrome","chrome-extension"],"created_at":"2024-11-28T15:05:55.719Z","updated_at":"2025-04-06T01:06:43.725Z","avatar_url":"https://github.com/KeithHenry.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Chrome Extension Async\r\n[![npm version](http://img.shields.io/npm/v/chrome-extension-async.svg)](https://www.npmjs.com/package/chrome-extension-async)\r\n[![bower version](https://img.shields.io/bower/v/chrome-extension-async.svg)](https://github.com/KeithHenry/chromeExtensionAsync/releases)\r\n\r\nPromise wrapper for the Chrome extension API so that it can be used with async/await rather than callbacks\r\n\r\nThe [Extension API](https://developer.chrome.com/extensions) provided by Chrome uses callbacks.\r\nHowever, Chrome now supports `async` and `await` keywords.\r\n\r\nThis library wraps Chrome extension API callback methods in promises, so that they can be called with `async` and `await`.\r\n\r\nOnce activated against the Chrome API each callback function gets a `Promise` version.\r\n\r\nChrome supports ES2017 syntax, so in extensions we can take full advantage of it.\r\n\r\n## Installation\r\nUse bower\r\n```\r\nbower install chrome-extension-async\r\n```\r\n\r\nOr [npm](https://www.npmjs.com/package/chrome-extension-async)\r\n```\r\nnpm i chrome-extension-async\r\n```\r\n\r\nOr [download](chrome-extension-async.js) `chrome-extension-async.js` file and include it directly:\r\n```html\r\n\u003cscript type=\"text/javascript\" src=\"chrome-extension-async.js\"\u003e\u003c/script\u003e\r\n```\r\n\r\nTypeScript definitions for the altered API are in [`chrome-extension-async.d.ts`](chrome-extension-async.d.ts)\r\n\r\nYou must reference [`chrome-extension-async.js`](chrome-extension-async.js) before your code attempts to use the features of this, as it needs to run across the Chrome API before you call it. `\u003cscript async\u003e` is not currently supported, but you can use `\u003cscript defer\u003e` so long as the scripts that use this are also `defer` and after it.\r\n\r\n## Examples\r\nUsing the basic Chrome API, let's:\r\n- Get the current active tab\r\n- Execute a script in that tab\r\n- Do something with the first result of the script\r\n\r\n```javascript\r\nfunction startDoSomething(script, callback) {\r\n    // Fire off the tabs query and continue in the callback\r\n    chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {\r\n\r\n        // Check API for any errors thrown\r\n        if (chrome.runtime.lastError) {\r\n            // Handle errors from chrome.tabs.query\r\n        }\r\n        else {\r\n            var activeTab = tabs[0];\r\n\r\n            // Fire off the injected script and continue in the callback\r\n            chrome.tabs.executeScript(activeTab.id, { code: script }, function(results) {\r\n\r\n                // Check API for any errors thrown, again\r\n                if (chrome.runtime.lastError) {\r\n                    // Handle errors from chrome.tabs.executeScript\r\n                }\r\n                else {\r\n                    var firstScriptResult = results[0];\r\n                    callback(firstScriptResult);\r\n                }\r\n            });\r\n        }\r\n    });\r\n}\r\n```\r\n\r\nThis works, but the nested callbacks are painful to debug and maintain, and they can quickly lead to 'callback hell'.\r\n\r\nInstead, with this library, we can use `await`:\r\n\r\n```javascript\r\nasync function doSomething(script) {\r\n    try {\r\n        // Query the tabs and continue once we have the result\r\n        const tabs = await chrome.tabs.query({ active: true, currentWindow: true });\r\n        const activeTab = tabs[0];\r\n\r\n        // Execute the injected script and continue once we have the result\r\n        const results = await chrome.tabs.executeScript(activeTab.id, { code: script });\r\n        const firstScriptResult = results[0];\r\n        return firstScriptResult;\r\n    }\r\n    catch(err) {\r\n        // Handle errors from chrome.tabs.query, chrome.tabs.executeScript or my code\r\n    }\r\n}\r\n\r\n// If you want to use the same callback you can use Promise syntax too:\r\ndoSomething(script).then(callback);\r\n```\r\n\r\nSome callbacks take multiple parameters - in these cases the `Promise` will be a combined object:\r\n\r\n```javascript\r\nasync function checkUpdate() {\r\n    try {\r\n        // API is chrome.runtime.requestUpdateCheck(function (status, details) { ... });\r\n        // Instead we use deconstruction-assignment and await\r\n        const { status, details } = await chrome.runtime.requestUpdateCheck();\r\n        alert(`Status: ${status}\\nDetails: ${JSON.stringify(details)}`);\r\n    }\r\n    catch(err) {\r\n        // Handle errors from chrome.runtime.requestUpdateCheck or my code\r\n    }\r\n}\r\n```\r\n\r\nThis also includes a check against [`chrome.runtime.lastError`](https://developer.chrome.com/extensions/runtime#property-lastError), so that you can use `try`-`catch` to get exceptions thrown from the Chrome API.\r\n\r\n### Event Listener API\r\nThese are not included.\r\nFor instance `chrome.browserAction.onClicked.addListener` takes a callback function, but executes it every time the event fires.\r\nIt is not suitable for a `Promise` or `async` call.\r\n\r\n### Execute Injected Scripts Asynchronously With `chrome.tabs.executeAsyncFunction`\r\nNew in v3.2 is `chrome.tabs.executeAsyncFunction`, an enhancement to the tabs API that allows a popup or browser/page action to easily execute asynchronous code in a page. This:\r\n\r\n- Adds `chrome.runtime.sendMessage` to the injected script to return the result.\r\n- Uses `chrome.runtime.onMessage.addListener` to listen for the injected event.\r\n- Fires the script with `chrome.tabs.executeScript`.\r\n- Wraps the whole thing in a promise that resolves with the final result.\r\n- Adds all the relevant error handling by rejecting the promise.\r\n\r\n``` javascript\r\nconst scriptToExecute = async function() {\r\n    // await promises in the tab\r\n}\r\n\r\ntry {\r\n    // The await returns the complete result of the function\r\n    const results = await chrome.tabs.executeAsyncFunction(\r\n        activeTab.id,       // If null this will be the current tab\r\n        scriptToExecute,    // Will be .toString and applied to the {code:} property\r\n        123, 'foo');        // Additional parameters will be passed to scriptToExecute\r\n\r\n    // results now holds the output of the asynchronous code run in the page\r\n}\r\ncatch(err) {\r\n    // Any error either setting up or executing the script\r\n    // Note that errors from the page will be re-thrown copies\r\n}\r\n```\r\n\r\n`chrome.tabs.executeAsyncFunction` can take a `function`, `string`, or [`executeScript` details](https://developer.chrome.com/extensions/tabs#method-executeScript) with the `code` property set. The script must be `async` or return a `Promise`. Details with a `file` property are not supported. Scripts that output multiple values are not supported.\r\n\r\nUnlike `chrome.tabs.executeScript` this can take a `function`, but note that it just converts the function to a string to pass it. This means that it must be self contained (it cannot call other user defined functions) and it cannot be native (as many serialise to `function foobar() { [native code] }`).\r\n\r\nThis is held in its own file: [`execute-async-function.js`](execute-async-function.js):\r\n\r\n```html\r\n\u003cscript type=\"text/javascript\" src=\"execute-async-function.js\"\u003e\u003c/script\u003e\r\n```\r\n\r\nThis relies on a `chrome.runtime.onMessage.addListener` subscription, so it will fail if called from within a listener event.\r\n\r\n### Create and Reload Tabs with `chrome.tabs.createAndWait` and `chrome.tabs.reloadAndWait`\r\n\r\nNew in v3.4 is `chrome.tabs.createAndWait` and `chrome.tabs.reloadAndWait`. The normal `chrome.tabs.create` and `chrome.tabs.reload` functions execute their callbacks as soon as the tab is created, before the tab has finished loading. This makes it difficult to create or reload a tab, and then execute a content script on the page. `chrome.tabs.createAndWait` and `chrome.tabs.reloadAndWait` are an enhancement to the tabs API that waits until the tab has finished loading the url, and is ready to execute scripts. They pair great with `chrome.tabs.executeAsyncFunction`. \r\n\r\nThey:\r\n\r\n- Call `chrome.tabs.create` or `chrome.tabs.reload`, await the results, and grab the tab's id.\r\n- Use `chrome.tabs.onUpdated.addListener` to listen for the 'completed' status for the tab's id.\r\n- Wrap the whole thing in a promise that resolves with the final result.\r\n- Use `chrome.tabs.onRemoved.addListener` and `chrome.tabs.onReplaced.addListener` to detect if the tab is removed or replaced before the loading finishes, and rejects the promise with an Error.\r\n- Use an auto-timeout feature. If the page doesn't load in the specified milliseconds, or one of the three listeners is never called, the promise will be rejected with an Error. The value of the timeout is configurable with an optional parameter. The default value is 12e4 milliseconds (2 minutes). \r\n\r\n`chrome.tabs.createAndWait` takes in the same parameters as [chrome.tabs.create](https://developer.chrome.com/extensions/tabs#method-create) except for the callback, and returns an object containing the same properties as the parameters passed to the callback for the [chrome.tabs.onUpdated](https://developer.chrome.com/extensions/tabs#event-onUpdated) event.\r\n\r\n```javascript\r\ntry {\r\n    // Create a new tab and wait for it to finish loading.  The url will take 5 seconds to finish loading.\r\n    // Try closing the tab before it finishes loading, and you will see the error.\r\n    const {tabId, changeInfo, tab} = await chrome.tabs.createAndWait({ url: \"http://www.mocky.io/v2/5d59a32e3000006c2ed84c7a?mocky-delay=5000ms\", active:true });\r\n    // Now that it is finished loading, it is ready to execute content scripts.\r\n    const scriptResults = await chrome.tabs.executeAsyncFunction(tab.id, () =\u003e { alert('The tab finished loading.');} );\r\n    // Voila!  In two lines you've created a new tab, and executed a content script on it!\r\n}\r\ncatch (err) {\r\n    alert(err);\r\n}\r\n```\r\n`chrome.tabs.reloadAndWait` takes in the same parameters as [chrome.tabs.reload](https://developer.chrome.com/extensions/tabs#method-reload) except for the callback, and returns an object containing the same properties as the parameters passed to the callback for the [chrome.tabs.onUpdated](https://developer.chrome.com/extensions/tabs#event-onUpdated) event.\r\n```javascript\r\ntry {\r\n  // Get the current tab.\r\n  const tabs = await chrome.tabs.query({active: true, currentWindow: true});\r\n  const currentTab = tabs[0];\r\n  // The second parameter, reloadProperties is optional, and here it is omitted.\r\n  const {tabId, changeInfo, tab} = await chrome.tabs.reloadAndWait(currentTab.id);\r\n  const scriptResults = await chrome.tabs.executeAsyncFunction(tab.id, () =\u003e { alert('The tab finished reloading.');} );\r\n}\r\ncatch (err) {\r\n  alert(err);\r\n}\r\n```\r\n\r\nThese functions are held in: [`execute-async-function.js`](execute-async-function.js)\r\n\r\n## Supported APIs\r\nThis only 'promisifies' API functions that use callbacks and are not marked as deprecated.\r\nNo backwards compatibility is attempted.\r\n\r\nEach API is added manually as JS can't spot deprecated or functions with no callbacks itself.\r\n\r\nSupported API:\r\n\r\n- [chrome.alarms](https://developer.chrome.com/extensions/alarms)\r\n- [chrome.bookmarks](https://developer.chrome.com/extensions/bookmarks)\r\n- [chrome.browserAction](https://developer.chrome.com/extensions/browserAction)\r\n- [chrome.browsingData](https://developer.chrome.com/extensions/browsingData)\r\n- [chrome.commands](https://developer.chrome.com/extensions/commands#method-getAll)\r\n- [chrome.contentSettings ContentSetting](https://developer.chrome.com/extensions/contentSettings#type-ContentSetting)\r\n- [chrome.contextMenus](https://developer.chrome.com/extensions/contextMenus)\r\n- [chrome.cookies](https://developer.chrome.com/extensions/cookies)\r\n- [chrome.debugger](https://developer.chrome.com/extensions/debugger)\r\n- [chrome.desktopCapture](https://developer.chrome.com/extensions/desktopCapture)\r\n- [chrome.documentScan](https://developer.chrome.com/extensions/documentScan#method-scan)\r\n- [chrome.downloads](https://developer.chrome.com/extensions/downloads)\r\n- [chrome.enterprise.platformKeys](https://developer.chrome.com/extensions/fileBrowserHandler#method-selectFile)\r\n- [chrome.extension](https://developer.chrome.com/extensions/extension)\r\n- [chrome.fileBrowserHandler](https://developer.chrome.com/extensions/enterprise_platformKeys)\r\n- [chrome.fileSystemProvider](https://developer.chrome.com/extensions/fileSystemProvider)\r\n- [chrome.fontSettings](https://developer.chrome.com/extensions/fontSettings)\r\n- [chrome.gcm](https://developer.chrome.com/extensions/gcm)\r\n- [chrome.history](https://developer.chrome.com/extensions/history)\r\n- [chrome.i18n](https://developer.chrome.com/extensions/i18n)\r\n- [chrome.identity](https://developer.chrome.com/extensions/identity)\r\n- [chrome.idle](https://developer.chrome.com/extensions/idle#method-queryState)\r\n- [chrome.input.ime](https://developer.chrome.com/extensions/input_ime)\r\n- [chrome.management](https://developer.chrome.com/extensions/management)\r\n- [chrome.networking.config](https://developer.chrome.com/extensions/networking_config)\r\n- [chrome.notifications](https://developer.chrome.com/extensions/notifications)\r\n- [chrome.pageAction](https://developer.chrome.com/extensions/pageAction)\r\n- [chrome.pageCapture](https://developer.chrome.com/extensions/pageCapture#method-saveAsMHTML)\r\n- [chrome.permissions](https://developer.chrome.com/extensions/permissions)\r\n- [chrome.platformKeys](https://developer.chrome.com/extensions/platformKeys)\r\n- [chrome.runtime](https://developer.chrome.com/extensions/runtime)\r\n- [chrome.sessions](https://developer.chrome.com/extensions/sessions)\r\n- [chrome.socket](https://developer.chrome.com/extensions/socket)\r\n- [chrome.sockets.tcp](https://developer.chrome.com/extensions/sockets_tcp)\r\n- [chrome.sockets.tcpServer](https://developer.chrome.com/extensions/sockets_tcpServer)\r\n- [chrome.sockets.udp](https://developer.chrome.com/extensions/sockets_udp)\r\n- [chrome.storage StorageArea](https://developer.chrome.com/extensions/storage#type-StorageArea)\r\n- [chrome.system.cpu](https://developer.chrome.com/extensions/system_cpu)\r\n- [chrome.system.memory](https://developer.chrome.com/extensions/system_memory)\r\n- [chrome.system.storage](https://developer.chrome.com/extensions/system_storage)\r\n- [chrome.tabCapture](https://developer.chrome.com/extensions/tabCapture)\r\n- [chrome.tabs](https://developer.chrome.com/extensions/tabs)\r\n- [chrome.topSites](https://developer.chrome.com/extensions/topSites#method-get)\r\n- [chrome.tts](https://developer.chrome.com/extensions/tts)\r\n- [chrome.types](https://developer.chrome.com/extensions/types)\r\n- [chrome.wallpaper](https://developer.chrome.com/extensions/wallpaper#method-setWallpaper)\r\n- [chrome.webNavigation](https://developer.chrome.com/extensions/webNavigation)\r\n- [chrome.windows](https://developer.chrome.com/extensions/windows)\r\n\r\nPull requests with additional API gratefully received.\r\n\r\n## ES5 Build\r\nNote that you can use an `ES5` build version of \"Chrome Extension Async\".\r\n```\r\nexecute-async-function.es5.js\r\n```\r\nSometimes your application has a build process that requires you to use 3rd party libraries that published with `ES5` code.  \r\nFor example, [create-react-app](https://github.com/facebook/create-react-app) will [break the build and minification process](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#npm-run-build-fails-to-minify) if one of your dependencies is not published as standard `ES5` code.  \r\n\r\n## Release Notes\r\n\r\n### v3.4\r\nv3.4 adds `chrome.tabs.createAndWait` and `chrome.tabs.reloadAndWait`; this is backwards compatible and opt-in functionality.\r\n\r\n#### v3.4.1\r\nFixes an issue with the timeout message.\r\n\r\n### v3.3\r\nv3.3 adds `execute-async-function.es5.js` transpiled ES5 version for toolchains that depend on the older JS syntax.\r\n\r\n#### v3.3.1\r\nThis addresses a breaking change in `chrome.storage` and fixes _TypeError: Illegal invocation: Function must be called on an object of type StorageArea_ exceptions.\r\n\r\n#### v3.3.2\r\nFixed bug calling `chrome.identity.getRedirectURL`\r\n\r\n### v3.2\r\nv3.2 adds `chrome.tabs.executeAsyncFunction`; this is backwards compatible and opt-in functionality.\r\n\r\n### v3 Changes\r\nv3 introduces a breaking change from v1 and v2: now the original Chrome API is wrapped by an identical method that can be called with either old or new syntax.\r\nCallbacks can still be used on the same methods, and will fire before the promise resolves.\r\nAny error thrown inside the callback function will cause the promise to reject.\r\n\r\nYou can use both a callback and `await` if you want to work with existing API code, but also want the `try`-`catch` support:\r\n\r\n```javascript\r\nasync function startDoSomethingHybrid(callback) {\r\n    try{\r\n        // Using await means any exception is passed to the catch, even from the callback\r\n        await chrome.tabs.query({ active: true, currentWindow: true }, tabs =\u003e callback(tabs[0]));\r\n    }\r\n    catch(err) {\r\n        // Handle errors thrown by the API or by the callback\r\n    }\r\n}\r\n```\r\n\r\nOlder versions added a `...Async` suffix to either the function (2.0.0) or the API class (1.0.0). These are still available on bower (but not npm) and are not maintained.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkeithhenry%2Fchromeextensionasync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkeithhenry%2Fchromeextensionasync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkeithhenry%2Fchromeextensionasync/lists"}