0

I'm unsure on how to build this list (which is a string) and then returning as one complete string. I've worked past my last issue but I think this one is realy bugging me. buildItem() should iterate through item, and then recursively build a list while getting the totalCost from another callback. I know it works asynchronously...

buildItem(data, function(html){
    $('#nestable ol').append(html);
});

Should append the 'final' html string that's created from being appended throughout the file.

function buildItem(item, callback) {
    getTotalCost(item, function(totalCost) {
        var html = "<li class='dd-item' data-id='" + item.id + "' data-email='" + item.email + "' data-title='" + item.corporateTitle + "' data-name='" + item.firstName + " " + item.lastName + "' id='" + item.id + "'>";
        if (item.children && item.children.length > 0) {
            html += "<ol class='dd-list'>";
            $.each(item.children, function (index, sub) {
                buildItem(item, function(subHtml){
                    html += subHtml;
                })
            })
            html += "</ol>";
        }
        html += "</li>";
        callback(html);
    });
}

I know that

buildItem(item, function(subHtml){
    html += subHtml;
})

shouldn't work since javascript is asynchronous. I'm just not sure on how to return from a recursive function? If I were to do something like

buildItem(item, function(subHtml){
    callback(subHtml);
})

You'll get duplicate values because you'll have the starting value and it's children, but since you're also calling it back you'll get the children outside of the starting value. So it'll look like

1
     a
     b
     c
     d
     e
a
b
c
d
e

So what's the best way to approach a solution? I was thinking of making another function, hypothetically a buildChild(sub) that returned html, but the same issue with asynchronous is going to come up where the return will be undefined. I've read some of the threads where you can handle asynchronous values with callbacks, but I'm not sure on how to do it with recursion here.

getTotalCost is another callback function that shouldn't mean much, I removed the line by accident but I just need the totalCost from a database.

function getTotalCost(item, callback) {
    $.ajax({
        dataType: "json",
        url: "/retrieveData.do?item=" + item.email,
        success: function(data) {
            var totalCost = 0;
            for (var i = 0; i < data.length; i++) {
                totalCost += parseFloat(data[i].cost);
            }
            callback(totalCost);
        }
    });
}
CHom
  • 3
  • 3

2 Answers2

0

You can simplify this with promises and async functions:

async function getTotalCost(item) {
  const data = await Promise.resolve($.ajax({
    dataType: "json",
    url: "/retrieveData.do?item=" + item.email
  }));
  return data.reduce((acc, next) => acc + next.cost, 0);
}

async function buildItem(item) {
  const totalCost = await getTotalCost(item);
  let html = `<li class="dd-item" data-id="${item.id}" data-email="${item.email}" data-title="${item.corporateTitle}" data-name="${item.firstName} ${item.lastName}" id="${item.id}">`;
  if (item.children && item.children.length > 0) {
    html += '<ol class="dd-list">';
    for (const childItem of item.children) {
      html += await buildItem(childItem);
    }
    html += "</ol>";
  }
  html += "</li>";
  return html;
}

Unfortunately, async functions aren't supported by all browsers yet, so you'll have to use Babel to transpile your code.

I also added some new ES6 features: arrow functions, const and template literals.

Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
  • I suppose I could, but I'm working with IE11 and the code that gets transpiled doesn't look simple to maintain. I was hoping that there would be a solution to build upon what I already have. – CHom Jun 06 '17 at 15:50
  • @CHom What do you mean? You don't maintain transpiled code – it's only for the browser. – Michał Perłakowski Jun 06 '17 at 15:51
  • I'm developing to be used on a large scale website and as one of the developers on this project I don't think I want to make such a large change to how code is done for one particular section. I'd like to use it if I weren't on this project, though. – CHom Jun 06 '17 at 15:56
  • @CHom Then you can use the [`series` function of async library](https://caolan.github.io/async/docs.html#series). – Michał Perłakowski Jun 06 '17 at 15:59
  • Does this mean that there's no viable solution outside of using another library? Just wondering. – CHom Jun 06 '17 at 16:15
  • @CHom You can implement the `series` function yourself, but that would essentially be reinventing the wheel. If you really wanted to, you could also solve this without any helper functions, but then your code would much less readable. – Michał Perłakowski Jun 06 '17 at 16:20
0

You can mix slow ajax requests with logic and recursion if you execute your code via synchronous executor nsynjs.

Step 1. Write your logic as if it was synchronous, and place it into function:

function process(item) {
    function getTotalCost(item) {
        var data = jQueryGetJSON(nsynjsCtx, "/retrieveData.do?item=" + item.email).data;
        var totalCost = 0;
        for (var i = 0; i < data.length; i++) {
            totalCost += parseFloat(data[i].cost);
        }
        return totalCost;
    };

    function buildItem(item) {
        const totalCost = getTotalCost(item);
        var html = "<li class='dd-item' data-id='" + item.id + "' data-email='" + item.email + "' data-title='" + item.corporateTitle + "' data-name='" + item.firstName + " " + item.lastName + "' id='" + item.id + "'>";

        if (item.children && item.children.length > 0) {
            html += '<ol class="dd-list">';
            for (var i=0; i<item.children.length; i++)
                html += buildItem(item.children[i]);
            html += "</ol>";
        }
        html += "</li>";
        return html;
    };

    return buildItem(item);
};

Step 2: run it via nsynjs:

nsynjs.run(process,{},item,function (itemHTML) {
    console.log("all done",itemHTML);
});

Please see more examples here: https://github.com/amaksr/nsynjs/tree/master/examples

amaksr
  • 7,555
  • 2
  • 16
  • 17