/** * Sending a message to target tab repeatedly until the response is not undefined. * @param {object} tab the table where to send the message * @param {object} req the request data. * @param {function} dataChecker (reulst:any, err:error, tryCount:number) => any. * Check and decide what value finally returns. * Return undefined to make 'sendMessage' retry. * Return MSG_USER_ABORT to cancel this promise. * @param {number} interval retry interval, default: 500ms. * @param {number} limit retry limit, default: 0, no limit. * @param {string} log messages logged to console. * @return {Promise} a promise of the response. */ function sendMessage(tab, req, log, dataChecker, interval, limit = 0) { interval = interval || 500; limit = limit && !isNaN(limit) ? limit : 0; let count = 0; return new Promise((resolve, reject) => { loop(); async function loop() { logger.debug("Request for", req.action); let tabAvailable = await getTabByID(tab.id); if (!tabAvailable) { reject("Task interrupted due to the target tab is closed."); return; } if (limit && count >= limit) { reject(`sendMessage loop limit ${limit} reached.`); return; } count++; chrome.tabs.sendMessage(tab.id, req, async r => { // check error but do nothing. // do not interrupt promise chains even if error, or the task always fail when: // a tab is newly created, and the content scripts won't have time to initialize let err = chrome.runtime.lastError; let result = r; if (dataChecker) { result = await dataChecker(r, err, count); if (MSG_USER_ABORT.isEqual(result)) { reject(MSG_USER_ABORT.message); } } let flag = result !== undefined && result !== null; if (log) logger.info(log, flag ? '(OK)' : '(failed)'); if (flag) { resolve(result); } else { setTimeout(() => { loop(); }, interval); } }); } }); } chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) { if (!request.action || !request.action.startsWith(EXT_NAME)) { return; } switch (request.action) { case ACTION_UPLOAD_STATE: sendResponse('recieved!'); __EXTRACTOR_STATE__ = request.state; logger.info(`State (${request.name}) recieved. To load it: some_var = new Extractor().load()`); break; default: sendResponse("Request not supported."); break; } });