0

Can't get my getJSON function to return the array. I've been trying to use await/async but I'm doing something wrong.

.map and .filter should be synchronous but I've tried putting await on them as well.

$(function() {
    $("#roll").click(async function() {
    var x = await getTreasure()
    chrome.extension.getBackgroundPage().console.log(x)
    });
});

function getTreasure() {
    var sizeValue = $('input[name=size]:checked').val();
    var crValue = $('input[name=challenge]:checked').val();
    var die = Math.floor((Math.random() * 100) + 1);
    var url = "";

    if (sizeValue == "individual") {
        url = chrome.runtime.getURL("treasure_individual.json");
    } else {
        url = chrome.runtime.getURL("treasure_horde.json");
    };
    $.getJSON(url, function(data) {
        var match = data.treasure.filter(function (e) {
            return e.cr == crValue;
        });
        for (i in match[0].roll) {
            var str = match[0].roll[i].d100;
            var arr = str.match(/([0-9]+)/g);
            var levels = $.map(arr, function (x) { 
                return parseInt(x, 10); 
            });

            if (die == levels[0] || die >= levels[0] && die <= levels[1]) {
                chrome.extension.getBackgroundPage().console.log(levels);
                return levels;
            } else {
                return die;
            };
        };
    });
};

Edit: Ok, didn't understand await still needed a Promise. But I'm still not getting it. Tried adding the return Promise around the getJson function but it's still not returning levels.

function getTreasure() {
    var sizeValue = $('input[name=size]:checked').val();
    var crValue = $('input[name=challenge]:checked').val();
    var die = Math.floor((Math.random() * 100) + 1);
    var url = "";

    if (sizeValue == "individual") {
        url = chrome.runtime.getURL("treasure_individual.json");
    } else {
        url = chrome.runtime.getURL("treasure_horde.json");
    };

    return new Promise(resolve => {
    $.getJSON(url, function(data) {
        var match = data.treasure.filter(function (e) {
            return e.cr == crValue;
        });
        for (i in match[0].roll) {
            var str = match[0].roll[i].d100;
            var arr = str.match(/([0-9]+)/g);
            var levels = $.map(arr, function (x) { 
                return parseInt(x, 10); 
            });

            if (die == levels[0] || die >= levels[0] && die <= levels[1]) {
                //chrome.extension.getBackgroundPage().console.log(levels);
                return levels;
            };
            };
        });
    });
};
Ivar
  • 6,138
  • 12
  • 49
  • 61
bigbucky
  • 407
  • 6
  • 20
  • 1
    `await` only waits on a `Promise`. Your `getTreasure()` doesn't return one. – Ivar Nov 24 '18 at 20:44
  • 1
    You will want to use `return $.getJSON(url).then(function(data) { …`. Maybe replace that `then` call with an `await` as well. – Bergi Nov 24 '18 at 20:46
  • You never call `resolve`. But instead of `new Promise`, just return `$.getJSON(...........).promise()`. – trincot Nov 24 '18 at 22:30
  • `await` and `async` are wrapped around Promises, those need to be resolved or rejected. Whenever you do a request: `new Promise((resolve, reject) => {/* your function body */})` Call `resolve('yourreturnvalue')` for a successful request, otherwise reject on error. – Xorifelse Nov 24 '18 at 22:34
  • 1
    @Xorifelse [No need to use `new Promise`](https://stackoverflow.com/a/31327725/1048572) – Bergi Nov 24 '18 at 22:49
  • 1
    @bigbucky, did you read the comments? Don't use `new Promise` here. It is bad practice! Also, don't put an answer inside your question. It makes any answers look stupid. Instead comment, vote and eventually: accept. – trincot Nov 24 '18 at 23:13
  • I read the comments but they were contradicting. `new Promise` worked when I used `resolve`. But I can't get `return $.getJSON(...........).promise()` to work. I can't get the object created in the `getJSON` function to return to the calling function. – bigbucky Nov 25 '18 at 06:16
  • If I do something like `return $.getJSON(......return levels;).promise()` I can't get `levels` back to `var x = await getTreasure()` – bigbucky Nov 25 '18 at 06:20
  • If you need to have a specific promised value, then use `then` on the deferred object that jQuery gives you. – trincot Nov 25 '18 at 08:15

1 Answers1

3

The way to return the promise is not by adding new Promise (which is an anti-pattern in this case), but to get the promise that jQuery already has for you:

    return $.getJSON(url, function(data) {
//  ^^^^^^
       // .......
    }).promise();
//    ^^^^^^^^^^

NB: in your second attempt you never called resolve.

If you need some specific data to be "promised", then chain a then instead of using the callback of $.getJSON. Like this:

    return $.getJSON(url).then(function(data) {
//  ^^^^^^               ^^^^^^
       // .......
       return levels;
    });
trincot
  • 317,000
  • 35
  • 244
  • 286
  • I tried this but I was getting an error saying `resolve` was not defined. I probably was putting it in the wrong scope. – bigbucky Nov 24 '18 at 23:08
  • You should not have any `resolve`. That only makes sense when you have `new Promise`, which you should not need (it is bad practice) – trincot Nov 24 '18 at 23:10
  • Is this just because I'm using `getJSON` because I haven't seen `promise()` in the other stuff I've read. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function – bigbucky Nov 25 '18 at 06:55
  • `promise()` is a jQuery method on *deferred* objects (`$.getJSON` returns a *deferred*). See [jQuery documentation of `.promise()`](https://api.jquery.com/deferred.promise/). – trincot Nov 25 '18 at 08:06
  • If you need to have a promise for *specific* data, then use [`then`](https://api.jquery.com/deferred.then/). I've now added this possibility to my answer. – trincot Nov 25 '18 at 08:13
  • Thank you! That did it. Thanks for your patience. I learned a lot. – bigbucky Nov 25 '18 at 18:47