so I'm learning about async-await in javascript and when I try to do this callback hell ( I know it is not the best practice but I have to learn It )
the second time I call the function it keeps calling itself ( infinity loop )
u can run the code to understand more cuz nothing looks wrong to me I spend the last 2 days trying to understand the problem but I end up here
code :
const xhr = new XMLHttpRequest();
const url = new URL("https://www.breakingbadapi.com/api/quotes");
const moveiData = (link, callBack) => {
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === 4 && xhr.status == 200) {
} else if (xhr.readyState === 4) {
callBack("could not fetch data");
}
});
xhr.open("GET", link);
xhr.send();
};
moveiData(url, (response) => {
console.log(response);
moveiData(url, (response) => {
console.log(response);
});
});
const url = new URL("https://www.breakingbadapi.com/api/quotes");
const moveiData = (link, callBack) => {
const xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === 4 && xhr.status == 200) {
} else if (xhr.readyState === 4) {
callBack("could not fetch data");
}
});
xhr.open("GET", link);
xhr.send();
};
moveiData(url, (response) => {
console.log(response);
});
You have three options in that issue.
1- You have added a new event listener and you should remove it when you take response.
const xhr = new XMLHttpRequest();
const url = new URL("https://www.breakingbadapi.com/api/quotes");
const moveiData = (link, callBack) => {
const handler = () => {
if (xhr.readyState === 4 && xhr.status == 200) {
xhr.removeEventListener("readystatechange", handler);
callBack(JSON.parse(xhr.response));
} else if (xhr.readyState === 4) {
callBack("could not fetch data");
}
};
const a = xhr.addEventListener("readystatechange", handler);
xhr.open("GET", link);
xhr.send();
};
moveiData(url, (response) => {
console.log(response);
moveiData(url, (response) => {
console.log(response);
});
});
2- Use onreadystatechange callback.
const xhr = new XMLHttpRequest();
const url = new URL("https://www.breakingbadapi.com/api/quotes");
const moveiData = (link, callBack) => {
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status == 200) {
callBack(JSON.parse(xhr.response));
} else if (xhr.readyState === 4) {
callBack("could not fetch data");
}
};
xhr.open("GET", link);
xhr.send();
};
moveiData(url, (response) => {
console.log(response);
moveiData(url, (response) => {
console.log(response);
});
});
3- Declare new XMLHttpRequest for every function.
const url = new URL('https://www.breakingbadapi.com/api/quotes');
const moveiData = (link, callBack) => {
const xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', () => {
if (xhr.readyState === 4 && xhr.status == 200) {
callBack(JSON.parse(xhr.response));
} else if (xhr.readyState === 4) {
callBack('could not fetch data');
}
});
xhr.open("GET", link);
xhr.send();
};
moveiData(url, function (response) {
console.log(response);
moveiData(url, function (response) {
console.log(response);
});
});
Don't reuse the same XMLHttpRequest object. Declare a new one every time you call the function, and make sure you return some data in the callback if there's a positive response.
const url = new URL('https://www.breakingbadapi.com/api/quotes');
const moveiData = (link, callBack) => {
const xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', () => {
if (xhr.readyState === 4 && xhr.status == 200) {
callBack(JSON.parse(xhr.response));
} else if (xhr.readyState === 4) {
callBack('could not fetch data');
}
});
xhr.open("GET", link);
xhr.send();
};
moveiData(url, function (response) {
console.log(response);
moveiData(url, function (response) {
console.log(response);
});
});
You may find the relatively new fetch API and the async/await process a little more rewarding.
const url = 'https://www.breakingbadapi.com/api/quotes';
function getData(url) {
return fetch(url);
}
async function main() {
try {
const res = await getData(url);
console.log(await res.json());
} catch (err) {
console.log(err);
}
}
main();
Related
I have this script that I use to import some data from an API, and into my database. Since this process is very time consuming, it often times out because on some of the items processed there is a lot of data to process..
I came with this solution a while ago, using promises, to first do the request to the API, then after it finishes I would prepare the data and put it into a temporary csv file, then I would fire another request to split that file into multiple smaller files, then... you get the idea... it was working, but I need to add to it some extra requests, I just can't make it work... I probably just need to simplify my logic.
Anyone can help me improve this code to make it easier to add those extra requests and keep it sending one request after the other?
This is the (over simplified) script in question:
window.importTrialsScripts = {};
window.addEventListener('DOMContentLoaded', function() {
importTrialsScripts.app.initialize();
});
(function(importTrialsScripts, document, $) {
importTrialsScripts = importTrialsScripts || {};
const app = {
ajaxurl: 'myajaxurl',
initialize: function() {
this.submitHandler();
},
submitHandler: function() {
const self = this;
document.querySelector('#start-import').addEventListener('click', function() {
self.pullTrialsFromApi();
});
},
pullTrialsFromApi: function() {
let data = new FormData();
data.append('action', 'pull_trials_from_api');
[123, 456, 789].forEach(function(str) {
data.append('ids[]', str);
});
this.startPullingTrials(data);
},
startPullingTrials: function(data) {
const self = this;
let promise = new Promise(function(resolve, reject) {
self.sendAjaxRequest(data, function() {
if (this.status === 200) {
const response = JSON.parse(this.response);
if (! response.success) {
alert('The API could not be reached. Please try again.');
console.error('Error!!', response);
return;
}
resolve(response.data);
}
else {
console.error('there was an error in the request', this);
reject(this);
}
});
});
promise.then(function(chunks) {
const processingChunks = Object.values(chunks).map(function(chunk) {
return self.processChunk(chunk);
});
Promise.all(processingChunks).then(function (processedTrials) {
console.log('finished', processedTrials);
});
}, function(err) {
console.error('promise rejected', err);
});
},
processChunk: function(chunkTrials) {
const self = this;
let data = new FormData();
data.append('action', 'process_trials_chunk');
Object.values(chunkTrials).forEach(function(chunk) {
data.append('chunk[]', JSON.stringify(chunk));
});
return new Promise(function(resolve, reject) {
self.sendAjaxRequest(data, function() {
if (this.status === 200) {
const response = JSON.parse(this.response);
if (! response.success) {
console.error('Error!!', response.data);
return;
}
resolve(response.data);
}
else {
console.log('there was an error in the request', this);
reject(this);
}
});
});
},
splitToMultipleFiles: function() {
const self = this;
const data = new FormData();
data.append('action', 'split_location_files');
return new Promise(function(resolve, reject) {
self.sendAjaxRequest(data, function() {
if (this.status === 200) {
const response = JSON.parse(this.response);
if ( ! response.success ) {
console.error('Error!!', response.data);
return;
}
resolve(response.data.files);
}
else {
console.log('there was an error in the request', this);
reject(this);
}
});
});
},
processLocation: function(file) {
const self = this;
let data = new FormData();
data.append('action', 'process_location_data');
data.append('file', file);
return new Promise(function(resolve, reject) {
self.sendAjaxRequest(data, function() {
if ( this.status === 200 ) {
const response = JSON.parse(this.response);
if (! response.success) {
console.error('Error!!', response.data);
return;
}
resolve(response.data);
}
else {
console.log('there was an error in the request', this);
reject(this);
}
});
});
},
sendAjaxRequest: function(data, callback) {
const self = this;
let xhr = new XMLHttpRequest();
xhr.open('POST', ajaxurl);
xhr.onload = callback;
xhr.addEventListener('timeout', function(e) {
console.error('the request has timed out', e);
});
xhr.addEventListener('error', function(e) {
console.error('the request returned an error', e);
});
xhr.addEventListener('abort', function(e) {
console.error('the request was aborted', e);
});
xhr.send(data);
},
};
$.extend(importTrialsScripts, {
app: app
});
}).apply(this, [window.importTrialsScripts, document, jQuery]);
After Adding data to database with Ajax calls my TodosList component must refresh and show the all todos with the new one added after updating it using another Ajax call (that calls is a php SELECT * statement), but that doesn't happen; sometimes it shows the new data and sometimes only old data until I refresh page or added a new todo.
Isn't the role of setState() is to update our View with NEW DATA? How do I get that to work? (I added a callback to setState() that it's Console.log(this.state.todos) to see what happen but as I mentioned sometimes todos array got updated and sometimes it doesn't it shows only old data until page refreshed manually or added a new todo) I used Axios, and XMLHTTPRequest with promise but same problem .(in console screenshot u can see how array length is not updated sometimes)
constructor(props) {
super(props);
this.state = {
todos: [],
currentTodoText: '',
currentTodoEditText: ''
};
this.todoFormTfHandler = this.todoFormTfHandler.bind(this);
this.todoFormBtnHandler = this.todoFormBtnHandler.bind(this);
this.todoCheckHandler = this.todoCheckHandler.bind(this);
this.todoEditTextHandler = this.todoEditTextHandler.bind(this);
this.todoEditHandler = this.todoEditHandler.bind(this);
this.todoRemoveCancelHandler = this.todoRemoveCancelHandler.bind(this);
this.makeRequest = this.makeRequest.bind(this);
}
//called whenever app started and when a todo is added to database to refresh results
getDataFromDataBase() {
//get all registred todos
//let requestResult = [];
/*const xhr = new XMLHttpRequest();
return new Promise(function(resolve, reject) {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const requestResult = JSON.parse(xhr.responseText);
resolve(requestResult);
/*this.setState({ todos: requestResult });
console.log(requestResult);*/
/* } else {
reject({ status: xhr.status, statusText: xhr.statusText });
}
};
xhr.open('POST', 'http://localhost/React_ToDo_APP/to-do/src/PHPFiles/SelectAllTodos.php', true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send();
});*/
//console.log(requestResult);
//this.setState({ todos: requestResult });
axios.post('http://localhost/React_ToDo_APP/to-do/src/PHPFiles/SelectAllTodos.php').then((res) => {
this.setState({ todos: res.data }, () => {
console.log(this.state.todos);
});
});
}
makeRequest = (url, method) => {
// Create the XHR request
var request = new XMLHttpRequest();
// Return it as a Promise
return new Promise(function(resolve, reject) {
// Setup our listener to process compeleted requests
request.onreadystatechange = function() {
// Only run if the request is complete
if (request.readyState !== 4) return;
// Process the response
if (request.status >= 200 && request.status < 300) {
// If successful
resolve(request.responseText);
} else {
// If failed
reject({
status: request.status,
statusText: request.statusText
});
}
};
// Setup our HTTP request
request.open(method || 'GET', url, true);
request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
// Send the request
request.send();
});
};
componentDidMount() {
/*this.getDataFromDataBase()
.then(function(data) {
this.setState({ todos: data });
console.log('wttttf');
})
.catch(function(error) {
console.log(error);
console.log('lolo');
});*/
/*
this.makeRequest('http://localhost/React_ToDo_APP/to-do/src/PHPFiles/SelectAllTodos.php', 'POST')
.then((data) => {
this.setState({ todos: data });
})
.catch(function(error) {
console.log('Something went wrong', error);
});*/
this.getDataFromDataBase();
}
todoFormTfHandler(e) {
/*used to save whats writed in the field*/
//const tmpTodoText = e.target.value;
const tmpTodoText = e.target.value;
this.setState({ currentTodoText: tmpTodoText });
}
todoFormBtnHandler(e) {
if (this.state.currentTodoText !== '') {
const tmpTodo = {
//id: this.state.todos.length + 1,
text: this.state.currentTodoText,
isDoneChecked: false,
isEditable: false,
isRemoved: false
};
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
/*if (xhr.readyState === 4 && xhr.status === 200) {
}*/
};
const tmpTodoJSON = JSON.stringify(tmpTodo);
xhr.open('POST', 'http://localhost/React_ToDo_APP/to-do/src/PHPFiles/AddTodo.php', true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send('data=' + tmpTodoJSON);
/*const tmpTodos = this.state.todos;
tmpTodos.push(tmpTodo);
this.setState({ todos: tmpTodos });*/
this.getDataFromDataBase();
/*this.makeRequest('http://localhost/React_ToDo_APP/to-do/src/PHPFiles/SelectAllTodos.php', 'POST')
.then(function(data) {
this.setState({ todos: data });
})
.catch(function(error) {
console.log('Something went wrong', error);
});*/
const tmpTodoText = '';
this.setState({ currentTodoText: tmpTodoText });
}
}
I'm using Azure Storage to serve up static file blobs but I'd like to add a Cache-Control and Expires header to the files/blobs when served up to reduce bandwidth costs.
But I am using Node JS
How I do this?
This is what I've until now:
Simpler solution: jsfiddle.net/fbqsfap2
//https://myaccount.blob.core.windows.net/mycontainer/myblob?comp=properties
function updateBlob(blobName, containerName, accountName, properties) {
let xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE ) {
console.log(xmlhttp.status);
if (xmlhttp.status == 200) {
const updatedPropertiesStr = getPropertiesKeysStr(properties);
console.log("Update sucessfully ", updatedPropertiesStr);
}
else if (xmlhttp.status == 400) {
console.log('There was an error 400');
}
else {
console.log('Something else other than 200 was returned' , xmlhttp.status);
}
}
};
const url = "https://<account>.blob.core.windows.net/<container>/<blob>?cache-control='max-age=3600'";
xmlhttp.open("PUT", url, true);
xmlhttp.send();
}
function updateBlobPorperty(blobName, containerName, accountName, properties) {
let xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE ) {
console.log(xmlhttp.status);
if (xmlhttp.status == 200) {
const updatedPropertiesStr = getPropertiesKeysStr(properties);
console.log("Update sucessfully ", updatedPropertiesStr);
}
else if (xmlhttp.status == 400) {
console.log('There was an error 400');
}
else {
console.log('Something else other than 200 was returned' , xmlhttp.status);
}
}
};
const getParameters = object2getParameters(properties);
const url = `https://${accountName}/${containerName}/${blobName}?${getParameters}`;
xmlhttp.open("PUT", url, true);
xmlhttp.send();
}
function getPropertiesKeysStr(properties){
return Object.keys(properties).reduce((actual, next) => actual.concat(` ${next}`), "");
}
function object2getParameters(object){
let propertiesStr = "";
for (key of Object.keys(object)) {
let prop = `${key}=${object[key]}&`;
propertiesStr += prop;
}
propertiesStr = propertiesStr.substr(0, propertiesStr.length-1);
return propertiesStr;
}
Complete Solution using Node JS: jsfiddle.net/rhske0nj
const azure = require('azure-storage');
const got = require('got');
const Promise = require('promise');
// =============================== Consts Definitions ====================================
const AZURE_STORAGE =
{
ACCOUNT: "ACCOUNT",
ACCESS_KEY: "ACCESS_KEY"
}
const blobService = azure.createBlobService(AZURE_STORAGE.ACCOUNT, AZURE_STORAGE.ACCESS_KEY);
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// =============================== API to Azure Functions ================================
function getBlobsName(blobService, containerName){
return new Promise(function(resolve, reject){
blobService.listBlobsSegmented(containerName, null, function(err, result) {
if (err) {
reject(new Error(err));
} else {
resolve(result.entries.map(BlobResult => BlobResult.name))
}
})
});
}
function getContainersName(blobService){
return new Promise(function(resolve, reject){
blobService.listContainersSegmented(null, function(err, result) {
if (err) {
reject(new Error(err));
} else {
resolve(result.entries.map(ContainerResult => ContainerResult.name));
}
})
});
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// =================================== Main Function =====================================
function update(blobService){
getContainersName(blobService)
.then(containersName =>
containersName.forEach(cn =>
getBlobsName(blobService, cn)
.then(BlobsName =>
BlobsName.forEach(bn =>
updateBlobPorperty(bn, cn, AZURE_STORAGE.ACCOUNT,
{
headers: {
"Cache-Control": "max-age=2592000"
}
}
)
.then(console.log("Sucessfully updated"))
.catch(console.log)
//.cacth(console.log("Something failed"))
)
//console.log
)
.catch(err => console.log(err))
)
)
.catch(err => console.log(err))
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
update(blobService);
// =============================== Update Blob Block =====================================
/*
Example of Url:
https://homologsquidmedia.blob.core.windows.net/squid-pic/1.jpg?cache-control=max-age=3600
*/
function updateBlobPorperty(blobName, containerName, accountName, properties){
const cdnFileUrl = `https://${accountName}.azureedge.net/${containerName}/${blobName}`;
const fileUrl = `https://${accountName}.blob.core.windows.net/${containerName}/${blobName}`;
console.log(`fileUrl:> ${fileUrl}`);
//return got.put(fileUrl, properties);
return got.put(fileUrl, properties);
//return got.put(fileUrl);
}
function getPropertiesKeysStr(properties){
return Object.keys(properties).reduce((actual, next) => actual.concat(` ${next}`), "");
}
function object2getParameters(object){
let propertiesStr = "";
for (key of Object.keys(object)) {
let prop = `${key}=${object[key]}&`;
propertiesStr += prop;
}
propertiesStr = propertiesStr.substr(0, propertiesStr.length-1);
return propertiesStr;
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
I need to update for all my blobs in all my containers the property cache-control. If more information are needed please tell me. Tks.
The function you would want to call is setBlobProperties. This would set user-defined properties for the specified blob or snapshot.
Here is a sample code snippet that you can use in your application to persist the new Cache-Control value.
var properties = {};
properties.cacheControl = 'max-age=2592000';
blobService.setBlobProperties('<containerName>', '<blobName>', properties, function(error, result, response) {
if(!error) {
console.log('blob properties setted!');
}
})
The code will set the following properties for the Blob.
I come from here: How to make an AJAX call without jQuery?
And it is difficult to mock the Promise, the AJAX call or both.
I have tried so far with jasmine-ajax but it seems to have a bug... and apparently "is only meant for browser environment".
I have also tried to mock the XMLHttpRequest object but without success.
So, I am not sure what my options are here:
function get(url) {
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
req.open('GET', url);
req.onload = () => req.status === 200 ? resolve(req.response) : reject(Error(req.statusText));
req.onerror = (e) => reject(Error(`Network Error: ${e}`));
req.send();
});
}
Proof of concept how XMLHttpRequest can be mocked
function get(url) {
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
req.open('GET', url);
req.onload = () => req.status === 200 ? resolve(req.response) : reject(Error(req.statusText));
req.onerror = (e) => reject(Error(`Network Error: ${e}`));
req.send();
});
}
describe('XMLHttpRequest', function() {
var xhr
var urls
beforeEach(() => {
xhr = {
open: (_type_, _url_) => {
xhr.status = urls[_url_].status
xhr.response = urls[_url_].response
xhr.statusText = urls[_url_].statusText
},
send: () => {
setTimeout(xhr.onload, 1)
}
}
XMLHttpRequest = jasmine.createSpy(xhr).and.returnValue(xhr)
})
it('resolves query with `response`', function(done) {
urls = {
'http://foo/bar': {
response: 'some other value',
status: 200
}
};
get('http://foo/bar')
.then(r => expect(r).toBe('some other value'), e => expect(e).toBe(undefined))
.then(done)
})
it('rejects query with `statusText`', function(done) {
urls = {
'http://baz/quux': {
response: 'some other value',
status: 500,
statusText: 'some error'
}
};
get('http://baz/quux')
.then(r => expect(r).toBe(undefined), e => expect(e.message).toBe('some error'))
.then(done)
})
})
<script src="https://safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<link href="https://safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
Is there a way to unnest (or unchain) the chained anonymous functions below? I include my try after the script with nested anonymous functions. I want to define separately each function defined anonymously on its own to see clearly and understand where each function starts and ends and what they do. Thanks.
A script with nested anonymous functions that I want to separate (taken from here):
<html>
<script>
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {action: "getDOM"}, function(response) {
var firstParagraph = response.dom;
//}); moved to end to get the variable firstParagraph
var formData = new FormData();
formData.append("url", tab.url);
formData.append("title", tab.title);
formData.append("pitch", firstParagraph);
//formData.append("user_tag_list", "tag1, tag2");
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://ting-1.appspot.com/submithandlertest", true);
xhr.onreadystatechange = function (aEvt) {
if (xhr.readyState == 4) {
if (xhr.status == 200){
console.log("request 200-OK");
chrome.browserAction.setBadgeText ( { text: "done" } );
setTimeout(function () {
chrome.browserAction.setBadgeText( { text: "" } );
}, 2000);
}else{
console.log("connection error");
chrome.browserAction.setBadgeText ( { text: "ERR" } );
}
}
};
xhr.send(formData);
}); //chrome.tabs.sendRequest
});
});
</script>
</html>
My try to unnest anonymous function and reconstruct the script:
functionForSendRequest = function (response) {var firstParagraph = response.dom;
var formData = new FormData();
formData.append("url", tab.url);
formData.append("title", tab.title);
formData.append("pitch", firstParagraph);
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://ting-1.appspot.com/submithandlertest", true);
xhr.onreadystatechange = function (aEvt) {
if (xhr.readyState == 4){
if (xhr.status == 200){
console.log("request 200-OK");
chrome.browserAction.setBadgeText({text: "done" });
setTimeout(function(){
chrome.browserAction.setBadgeText({text: ""});}, 2000);}
else{console.log("connection error");
chrome.browserAction.setBadgeText({text: "ERR"});}}};
xhr.send(formData);}}
argumentToGetSelected = chrome.tabs.sendRequest(tab.id, {action: "getDOM"}, functionForSendRequest()}
...
functionForGetSelected = function (tab) {chrome.tabs.sendRequest(tab.id, {action: "getDOM"}, *function for getSelected goes here*)}
To see the logic clearly, instead of "unchaining" them why not just try to practice good indentation. Then you can visually follow each method according to its indentation level. Like this:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {action: "getDOM"}, function(response) {
var firstParagraph = response.dom;
var formData = new FormData();
formData.append("url", tab.url);
formData.append("title", tab.title);
formData.append("pitch", firstParagraph);
//formData.append("user_tag_list", "tag1, tag2");
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://ting-1.appspot.com/submithandlertest", true);
xhr.onreadystatechange = function (aEvt) {
if (xhr.readyState == 4) {
if (xhr.status == 200){
console.log("request 200-OK");
chrome.browserAction.setBadgeText ( { text: "done" } );
setTimeout(function () {
chrome.browserAction.setBadgeText( { text: "" } );
}, 2000);
} else {
console.log("connection error");
chrome.browserAction.setBadgeText ( { text: "ERR" } );
}
}
};
xhr.send(formData);
}); //chrome.tabs.sendRequest
}); // chrome.tabs.getSelected
}); // chrome.browserAction.onClicked.addListener
Or, if you want to "unchain", the only really obvious thing to do is to define that inner callback separately, like this:
var handle_request = function(response) {
var firstParagraph = response.dom;
var formData = new FormData();
formData.append("url", tab.url);
formData.append("title", tab.title);
formData.append("pitch", firstParagraph);
//formData.append("user_tag_list", "tag1, tag2");
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://ting-1.appspot.com/submithandlertest", true);
xhr.onreadystatechange = function (aEvt) {
if (xhr.readyState == 4) {
if (xhr.status == 200){
console.log("request 200-OK");
chrome.browserAction.setBadgeText ( { text: "done" } );
setTimeout(function () {
chrome.browserAction.setBadgeText( { text: "" } );
}, 2000);
} else {
console.log("connection error");
chrome.browserAction.setBadgeText ( { text: "ERR" } );
}
}
};
xhr.send(formData);
}
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {action: "getDOM"}, handle_requeest);
});
});