2

I have a lot of problems in my code because it is not synchronous. Here is an example of problem that i have in a chrome extension. This is my function

function getTranslation(a_data, callback)
{        
    var apiKey = '####'    
    var json_object = {};
    var url = '###';
    var xmlhttp;   
    var json_parsed = {};

    storage.get('data', function(items) 
    { 
        json_object = {  
            'text': a_data,
            'from' : items.data.from,
            'to' : items.data.to 
        };
        var json_data = JSON.stringify(json_object);

        if (window.XMLHttpRequest)
        {
            xmlhttp=new XMLHttpRequest();
        }
        else
        {
            xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
        }

        xmlhttp.open("POST", url, false);
        xmlhttp.setRequestHeader("Content-type","application/json");          
        xmlhttp.setRequestHeader("Authorization","##apiKey=" + apiKey);                      
        xmlhttp.setRequestHeader("X-Requested-With","XMLHttpRequest");                      
        xmlhttp.send(json_data);

        json_parsed = JSON.parse(xmlhttp.responseText);
        callback(json_parsed.translation);
    });                      
}

This is how i use getTranslation function in another function:

for (counter in toTranslateArray)
{
    getTranslation(toTranslateArray[counter],function(callback)
    {
        translated += callback;
        console.log(translated); //this is second and works
    });   
}
console.log(translated); //this is first and empty
//code depending on translated

Is it something wrong there ?

smotru
  • 433
  • 2
  • 8
  • 24
  • Put the assignment of `json_data` inside of the callback function. – apsillers Aug 13 '13 at 16:25
  • Ok but this is just a part of a function and didn`t solve my problem at all. After this I have an ajax request which use json_data. And i will get json_data undefined. – smotru Aug 13 '13 at 16:26
  • Put any code that needs to use `json_data` inside the callback as well, or inside a function that is called within the callback and receives `json_data` as an argument. If you put your Ajax call inside the callback, it will have access to `json_data`. – apsillers Aug 13 '13 at 16:27
  • just edited my post. the last return is mandatory. I can`t use only callback – smotru Aug 13 '13 at 16:37
  • By the way, in your code, `json_array` is neither JSON, nor an array – Ian Aug 13 '13 at 16:37
  • @smotru That's too bad, because `storage.get` seems to be asynchronous, therefore you have to deal with a callback in order to get its result. It shouldn't be too hard to reorganize your code and deal with asynchronicity the right way (both with `storage.get` and your AJAX) – Ian Aug 13 '13 at 16:38
  • @Ian yeah my error with json_array. And i just don`t see a way to return json_parsed.translation in other way. – smotru Aug 13 '13 at 16:42
  • @smotru First, move everything inside your `storage.get` callback function. Now: in the linked duplicate, search for the example with `filtered_response`. Treat your `getTranslation` function as the example's `foo` function. You need to pass a callback function into `gteTranslation`, i.e., `getTranslation(a_data, from, to, callback)`, and then replace `return json_parsed.translation` with `callback(json_parsed.translation)`. Finally, pass in a function as the fourth argument to `getTranslation` that takes the translated value as an argument and does something with it. – apsillers Aug 13 '13 at 19:12
  • @apsillers thank you for response. i just implemented as you said and this is the actual function declaration `function getTranslation(a_data, callback) {... storage.get(... callback(json_parsed.translation); )};` . And this is how i use it in another function `getTranslation(toTranslateArray[counter],trans); translated += trans;` . "trans is undefined" – smotru Aug 14 '13 at 12:16
  • just edited my description – smotru Aug 14 '13 at 12:58
  • Once again, you must move code that depends on the result of the callback *inside the callback*. Code that uses your callback value (which I'd suggest naming `callback_result`, not `callback`, since "callback" should refer to the *function* being passed) must be placed inside the callback. It's helpful to remember that asynchronous callbacks run *after* all other code has completed. Async functions can only run when the thread has run out of other things to do and is "resting". – apsillers Aug 14 '13 at 13:03
  • @apsillers there is a "for" statement. do you have any suggestions implementing this? i really need the lastest version of string "translated" – smotru Aug 14 '13 at 13:17
  • I think that question is different enough that it could be posted as a new question. However, it might be regarded as a duplicate of [Async functions inside forEach loops](http://stackoverflow.com/q/13214862/710446). This blog post seems to address this exact issue: http://geekabyte.blogspot.com/2013/04/callback-functions-in-loops-in.html – apsillers Aug 14 '13 at 13:28

2 Answers2

0

Create a custom event listener, where after you are done with stringifying json_data (done inside the the call back as suggested) it will then fire the event to do the ajax call. Go here to learn more about cusotm event JavaScript custom Event Listener

Community
  • 1
  • 1
Jack Thor
  • 1,554
  • 4
  • 24
  • 53
0

Since you are using sync XHR, instead of ajax, you need to use a sync function to save data, instead of chrome.storage, which is async.

In the chrome.storage documentation, one of its features is

  • It's asynchronous with bulk read and write operations, and therefore faster than the blocking and serial localStorage API.

But being blocking (and sync) is what you want, so why don't you change to that API instead?

Or even better:

Convert your getTranslation() function to be async. You would only need to add a third parameter that would be a callback, and use it inside the nested callbacks (because if you do this, you can also use ajax instead of sync XHR).

That way is the right thing, but if you feel lazy and want an easier way, just do the former and change chrome.storage to localStorage and you are done.

EDIT: I see you have already changed you function to be async. And it seems it works correctly, but you updated your question and you seem to have problems grasping why this line does not work:

console.log(translated); //this is first and empty

You need to understand how event oriented programming works. You may think that the line

for (counter in toTranslateArray)

which contains getTranslation means "do translation of every counter inside this toTranslateArray", but actually means "fire a translation event for every counter inside this toTranslateArray".

That means when that console.log get executed this tasks have just been fired; it does not wait for it to be finished. Therefore translated is empty in that moment. And that's normal, is executed async.

I don't know what you need to do with the translated var once is finished, but I would try to fire a different event once the last item of the array gets processed.

But anyway, what you need is to do is to study some tutorial or something about event oriented programming so all this makes more sense to you.

About the localStorage, I don't know, I found out about that as an alternative in the chrome.storage documentation, I really don't know how to use it in your case.

But since javascript is event oriented, I really recommend you to learn events instead of just going back to sync. But is up to you.

Community
  • 1
  • 1
chris-l
  • 2,802
  • 1
  • 18
  • 18