I'm getting this error over and over again and still can't figure out how to solve it. The sendResponse
function is being called before the analyzePageGuest
function has completed its execution, but I'm not able to fix it. The project I'm doing is an extension to detect phishing using the Virus Total API. This is what I tried: Can someone help me?
background.js
async function analyzePageGuest(tabId, changeInfo, tab) {
const url = tab.url
console.log('URL:', url)
let analysisIdValue
try {
const options = {
method: 'POST',
headers: {
accept: 'application/json',
'x-apikey':
'apikey',
'content-type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({ url: url }),
}
fetch(
'https://cors-anywhere.herokuapp.com/https://www.virustotal.com/api/v3/urls',
options
)
.then((response) => response.json())
.then((response) => {
console.log(response)
analysisIdValue = response.data.id
console.log(analysisIdValue)
})
.catch((err) => console.error(err))
// Wait for the analysis to complete using a polling mechanism
let resultResponse
let retries = 0
let resultJson
while (retries < 5) {
await new Promise((resolve) => setTimeout(resolve, 2000)) // wait for 2 seconds
resultResponse = await fetch(
`https://cors-anywhere.herokuapp.com/https://www.virustotal.com/api/v3/analyses/${analysisIdValue}`,
{
method: 'GET',
headers: {
'x-apikey':
'apikey',
},
}
)
resultJson = await resultResponse.json()
if (resultJson.data.attributes.status === 'completed') {
break
}
retries++
}
if (resultResponse.ok) {
const malicious = resultJson.data.attributes.stats.malicious > 0
const resultText = malicious
? 'Phishing detected!'
: 'Safe from phishing :)'
return resultText // Return the result text
} else {
return 'Error: Analysis failed' // Return an error message
}
} catch (error) {
console.error('Error analyzing page:', error)
return 'Error: Analysis failed' // Return an error message
}
}
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
if (request.action === 'analyzePageGuest') {
let tabId
if (sender.tab) {
tabId = sender.tab.id
} else {
const tabs = await chrome.tabs.query({
active: true,
currentWindow: true,
})
if (tabs.length > 0) {
tabId = tabs[0].id
} else {
console.log('No tabs found!')
sendResponse({ result: 'No tabs found!' }) // Return an error message
return false
}
}
if (tabId) {
console.log('Tab ID:', tabId)
const tab = await chrome.tabs.get(tabId)
console.log('Tab URL:', tab.url)
try {
const resultText = await analyzePageGuest(tabId, {}, tab)
console.log('Result text:', resultText)
sendResponse({ result: resultText })
} catch (error) {
console.error('Error analyzing page:', error)
sendResponse({ result: 'Error analyzing page' })
}
}
return true
}
})
popup.js
const button = document.querySelector('.button')
const resultElement = document.getElementById('result')
button.addEventListener('click', () => {
chrome.runtime.sendMessage({ action: 'analyzePageGuest' }, (response) => {
if (response && response.result) {
console.log('Received response:', response)
resultElement.innerText = response.result
}
})
setTimeout(() => {
if (!resultElement.innerText) {
resultElement.innerText =
'Analysis not completed. Please try again later.'
}
}, 4000)
})
popup.html
<div class="popup-container">
<span id="current-url"></span>
<button class="button">
<span class="submit">Analyze page</span>
<span class="loading"><i class="fas fa-circle-notch fa-spin"></i></i></span>
<span class="check"><i class="fa fa-check"></i></span>
</button>
<span id="result"></span>
</div>
I've already added return true to the asynchronous function, but it's not working..
So. To make your code work, you need to make a few very important changes.
Your listener function should not be asynchronous. According to the documentation:
Warning: Do not prepend async to the function. Prepending async changes the meaning to sending an asynchronous response using a promise, which is effectively the same as sendResponse(true).
It should look like this:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {})
And most importantly! You do everything right, You return true to process the asynchronous response, but you do not call the sendResponse
function correctly. sendResponse
function should be called after receiving the result in Promise.then(response => sendResponse(response))
.
I haven't looked at the logic behind your analyzePageGuest
function, but the final listener function should look something like this:
async function getActiveTab() {
const tabs = await chrome.tabs.query({ active: true });
let activeTab = null;
tabs?.forEach((tab) => {
if (!activeTab || tab.lastAccessed > activeTab.lastAccessed) {
activeTab = tab;
}
});
return activeTab;
}
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
switch (request.action) {
case 'analyzePageGuest':
getActiveTab().then(activeTab => {
analyzePageGuest(activeTab.id, {}, activeTab)
.then(response => sendResponse(response))
.catch(() => sendResponse({ result: 'Error analyzing page' }));
});
return true;
}
});
To get the url address of the active tab, you must have tabs
permission in manifest.json
:
// manifest.json
...
"permissions": [
"tabs"
]
...