简体   繁体   中英

Asynchronous javascript with 2 callbacks - how?

I need the code to flow like this: Get value from calcRoute (google maps distance) -> add to array(?)/add to output-string, until it has run through all values. Then append to listview.

var myArray = [];
$.get( "http://www.url.com/info.php", function( data ) {

        var output = "";
        var imgsrc = "";

         obj = jQuery.parseJSON(data);


        for (i = 0; i < obj.length; i++) 
            { 

                store=obj[i].store;

            for (j = 0; j < obj[i].products.length; j++) 
                { 
                    productname= obj[i].products[j].productname;
                    price=obj[i].products[j].price;

                    calcRoute(function(returnValue) {

                        myArray.push(returnValue);
                        console.log("Inside callback : " + flowcounter++);
                        console.log("Inside callback (Array): " + myArray);
                    }); 

                    console.log("Array has now (J-loop) : " + myArray);
                    console.log("Flowcounter has now (J-loop) : " + flowcounter++);
                    output+='<li> <img style="margin-left: 8px" width="80" height="80" alt="sample" src="img/logo.png" align="left"/> <h3>'+ productname+' '+price+'</h3><p>Ca:'+ myArray[i] +' km.</p> </li>';                 
                }

            }


        console.log("Append to list (Counter) : " + flowcounter++);
        console.log("Appen to list(Array): " + myArray);
        $("#prodlist").append(output).listview().listview('refresh');

            });

But right now it goes like this: J-loop -> append to listview -> calcRoute. So it doesn't have the values before executing the next thing. The problem here is obviously that I need to get a value, put it in output, and when they're all done, put in prodlist-append. Here is the calcRoute code:

        function calcRoute(callback) {

        var start = new google.maps.LatLng(55.613520,12.534539);
        var end = new google.maps.LatLng(55.713520,12.534539);

    var request = {
          origin: start,
          destination: end,
          travelMode: google.maps.TravelMode["DRIVING"]
      };
      directionsService.route(request, function(response, status) {
          if (status == google.maps.DirectionsStatus.OK) {
              directionsDisplay.setDirections(response);
          }
          var totalDistance = 0;
          var legs = response.routes[0].legs;
          for(var i=0; i<legs.length; ++i) {
             totalDistance += legs[i].distance.value;
          }
          totalDistance = totalDistance / 1000;
         totalDistance = totalDistance.toFixed(1);
         myArray.push(totalDistance);
         callback(totalDistance);
     });
    };

Added the logcat too: 流日志

Any help appreciated.

Look at Javascript Promises .

They allow you to chain asynchronous operations, like:

doThing1().then(doThing2()).then(doThing3());

or maybe more suitable for your purposes, Promise.all will wait until all operations complete before doing the next thing:

Promise.all([
    doThing1(),
    doThing2(),
    doThing3()
]).then(addToList(results));

Here's a jsfiddle that outlines the sort of thing you might do here. I don't know exactly what you're trying to do, but hopefully it's enough to get you moving.

Javascript:

var storeData = [
    {
        name: 'Store 1',
        location: {
            lat: 55.613520,
            lng: 12.534539
        },
        products: [
            {
                productname: "Product 1",
                price: 1.10
            },
            {
                productname: "Product 2",
                price: 2.20
            }
        ]
    },
    {
        name: 'Store 2',
        location: {
            lat: 55.613520,
            lng: 12.634539
        },
        products: [
            {
                productname: "Product 1.1",
                price: 1.11
            }
        ]
    }
];

// wraps a jquery ajax request for store data in a Promise
function getStores() {
    return new Promise(function(fulfill, reject) {

        // this is a jsfiddle ajax request
        // that just returns the json you send it (storeData).
        $.post( '/echo/json/', {
            json: JSON.stringify(storeData),
            delay: 1
        })
        .done(function(result) {
            return fulfill(result);    
        })
        .fail(function(reason) {
            return reject(reason);
        });
    });
}

function calcRoute(store) {
    return new Promise(function(fulfill, reject) {
        // call google here. faking it for the fiddle.
        return fulfill({
            result: 'Fake route result for store ' + store.name
        });
    });
}

function getRoutes(stores) {
    var promises = [];
    for (var i = 0; i < stores.length; i++) {
        promises.push(calcRoute(stores[i]));
    }
    return Promise.all(promises);
}

function updateDisplay(routes) {
    var list = document.getElementById('list');
    for (var i = 0; i < routes.length; i++ ) {
        console.log(routes[i].result);
        $(list).append('<li>' + routes[i].result + '</li>');
    }
}

function go() {
    return getStores()
    .then(getRoutes)
    .then(updateDisplay)
    .catch(function(reason) {
        console.error('Error %O', reason);
    });
}

$.ready(go());

You can use nested callbacks, or with some frameworks you can chain .then or .success using promises. Here is an interesting article about flattening nested callbacks to avoid "callback hell" http://solutionoptimist.com/2013/12/27/javascript-promise-chains-2/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM