简体   繁体   中英

Wait until an action ends before continuing execution of code

I explain my situation:

I am using HighCharts maps. The code forces a DrillDown (force to change map) and once done that, I must search inside all points of this new map (taking care that the DrillDown -doDrilldown()`- is not a function declared by me, it is a function property of HighCharts)

The problem is that I must wait until DrillDown ends before searching all points of this newly-loaded map. I have solved it using a SetTimeOut function:

var names = name.split(', ');

// Find point in countries map
this.internalChart.series[0].data.forEach(data => {
    if (data.name === names[1]) {
      data.doDrilldown() // FORCE DRILLDOWN. HERE I MUST WAIT
      // WAITING WITH A TIMEOUT IS WORKING
      setTimeout(() => {
        // CODE THAT MUST BE EXECUTED ONCE MAP IS LOADED
        this.state.loadedMaps.length > 0 && this.internalChart.options.drilldown.series[0].data.forEach(dataInside => {
          if (dataInside.name === names[0]) {
            this.setState({
              province: dataInside['hc-key'],
              loading: false
            });
          }
        });
      }, 1000)
    }
});

But this is not the correct way. I do not want always wait one second, I want that the code executes once load is done. I have tried using async/await but it is not working. The code I have tried is:

var names = name.split(', ');

// Find point in countries map
this.internalChart.series[0].data.forEach(data => {
    if (data.name === names[1]) {
        (async () => { await data.doDrilldown(); })() //HERE I MUST WAIT FOR DO THIS ACTION
            .then(()=>{
                // ONCE DONE BELOW ACTION, EXECUTE NEXT CODE:
                this.internalChart.options.drilldown.series[0].data.forEach(dataInside => {
                    if (dataInside.name === names[0]) {
                        this.setState({
                            //country: country,
                            province: dataInside['hc-key'],
                            loading: false
                        });
                    }
                });
            });
    }
});

Anyone knows how could I solve my problem?.

Thank you.


EDIT 1:

I have made an example which represents the problem. It can be found here: JSFiddle example


EDIT 2:

I need to know when .doDrilldown() finish. This function loads a new map and its data, so when the new map and new data would be loaded the code should continue executing. I am loading new drilldown series like:

// Import all map data
import(`./maps/${point['hc-key']}-all.geo`).then(mapData => {

  // Set data of the map
  var data = [];
  mapData.default.features.forEach((element, i) => {
    data.push({ 'hc-key': element.properties['hc-key'], 'name': element.properties.name, 'value': 0 });
  });

  // Create the new drilldown serie
  try {
    var drilldownSerie = {
      id: point['hc-key'],
      mapData: mapData.default,
      data: data,
      joinBy: 'hc-key',
      name: mapData.default.title,
      allowPointSelect: true,
      borderColor: '#ffffff',
      borderWidth: 1.2,
      states: {
        hover: {
          color: this.props.geoColor
        },
        select: {
          color: this.props.geoColor
        }
      },
      dataLabels: {
        enabled: true,
        format: '{point.name}'
      },
      point: {
        events: {
          click: (event) => {
            this.props.handleZoneChange(event.point);

            this.setState({
              selectedPoint: event.point
            });

            console.log("Click")
            console.log(this.state.selectedPoint)
            console.log("---")

          }
        }
      }
    };

    // Add the new drilldown serie
    this.internalChart.addSeriesAsDrilldown(point, drilldownSerie);

  } catch (err) {
    console.log(err.message)
  }
}).catch((err) => {
  console.log(err.message)
})

EDIT 3:

Here is full code, if were necessary.

import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import Highcharts from 'highcharts'
import HC_map from 'highcharts/modules/map'; //module
import HC_drilldown from 'highcharts/modules/drilldown'; //module
import HighchartsReact from 'highcharts-react-official';
import Button from '@material-ui/core/Button';
import worldMap, { dataWorldMap } from "./maps/worldMap.js";

HC_map(Highcharts); //init module
HC_drilldown(Highcharts) //init module

Highcharts.setOptions({
lang: {
    drillUpText: '◁ Volver a: {series.name}'
}
});

class GeoZoneChart extends Component {

constructor(props) {
    super(props);

    this.state = {
    loading: false,
    selectedPoint: ''
    };
}

geoZoneOptions = {
    chart: {
    map: worldMap,
    events: {
        drilldown: (event) => {
        this.handleMapChange(event.point);
        },
        drillup: (event) => {

        setTimeout(() => {
            console.log("DU")
            console.log(this.state.selectedPoint)
            console.log("---")
        }, 1000)

        },
    }
    },
    title: {
    text: ""
    },
    mapNavigation: {
    enabled: true,
    buttonOptions: {
        verticalAlign: 'bottom'
    }
    },
    colorAxis: {
    min: 0,
    max: 0,
    minColor: "#f7f7f7",
    maxColor: "#e2e2e2"
    },
    tooltip: {
    enabled: false
    },
    legend: {
    enabled: false
    },
    exporting: { enabled: false },
    series: [{
    mapData: worldMap,
    data: dataWorldMap,
    joinBy: 'hc-key',
    name: 'Mundo',
    allowPointSelect: true,
    borderColor: '#ffffff',
    borderWidth: 1.2,
    states: {
        hover: {
        color: this.props.geoColor
        },
        select: {
        color: this.props.geoColor
        }
    },
    dataLabels: {
        enabled: true,
        format: '{point.name}'
    },
    }, {
    name: 'Separators',
    type: 'mapline',
    color: 'silver',
    showInLegend: false,
    enableMouseTracking: false
    }],
    drilldown: {
    activeDataLabelStyle: {
        textDecoration: 'none',
        color: 'black'
    },
    series: []
    }
}

internalChart = undefined;

handleMapChange = (point) => {

    // Import all map data
    import(`./maps/${point['hc-key']}-all.geo`).then(mapData => {

    // Set data of the map
    var data = [];
    mapData.default.features.forEach((element, i) => {
        data.push({ 'hc-key': element.properties['hc-key'], 'name': element.properties.name, 'value': 0 });
    });

    // Create the new drilldown serie
    try {
        var drilldownSerie = {
        id: point['hc-key'],
        mapData: mapData.default,
        data: data,
        joinBy: 'hc-key',
        name: mapData.default.title,
        allowPointSelect: true,
        borderColor: '#ffffff',
        borderWidth: 1.2,
        states: {
            hover: {
            color: this.props.geoColor
            },
            select: {
            color: this.props.geoColor
            }
        },
        dataLabels: {
            enabled: true,
            format: '{point.name}'
        },
        point: {
            events: {
            click: (event) => {
                this.props.handleZoneChange(event.point);

                this.setState({
                selectedPoint: event.point
                });

                console.log("Click")
                console.log(this.state.selectedPoint)
                console.log("---")

            }
            }
        }
        };

        // Add the new drilldown serie
        this.internalChart.addSeriesAsDrilldown(point, drilldownSerie);

        // Select all map 
        //this.selectAll();
    } catch (err) {
        console.log(err.message)
    }
    }).catch((err) => {
    console.log(err.message)
    })
}

componentDidMount = () => {
    // Recover and set selected zone if exist
    this.props.defaultZone && this.selectRegionByName(this.props.defaultZone)
}

selectRegionByName = (name) => {
    if (!name.includes(', ')) {
    // Find point in global map
    this.internalChart.series[0].data.forEach(data => {
        if (data.name === name) {
        // Select the point 
        data.select(true, true)
        }
    });
    } else {
    var names = name.split(', ');
    // Find point in countries map
    this.internalChart.series[0].data.forEach(data => {
        if (data.name === names[1]) {
        // Drilldown on the map
        data.doDrilldown();
        setTimeout(() => {
            this.internalChart.series[0].data.forEach(dataInside => {
            if (dataInside.name === names[0]) {

                // Select the point
                dataInside.select(true, true)
            }
            });
        }, 100)
        }
    });
    }
}

afterChartCreated = (chart) => {
    this.internalChart = chart;
}

selectAll = () => {
    this.internalChart.series[0].data.forEach(data => {
    data.select(true, true);
    });

    this.props.handleSelectAllZones(this.internalChart.series[0].name);
}

componentWillUnmount = () => {
    this.internalChart.series[0].data.forEach(data => {
    data.select(false, false);
    });
}

render() {
    return (
    <Fragment>
        <HighchartsReact
        highcharts={Highcharts}
        constructorType={'mapChart'}
        options={this.geoZoneOptions}
        callback={this.afterChartCreated}
        />

        <Button
        variant="contained"
        color="primary"
        onClick={this.selectAll}
        style={{
            marginTop: -28,
            padding: 0,
            paddingLeft: 10,
            paddingRight: 10,
            float: "right",
            backgroundColor: this.props.geoColor,
            '&:hover': {
            backgroundColor: this.props.geoDarkColor
            }
        }}
        >
        Seleccionar todo
        </Button>
    </Fragment >
    );
}
}

GeoZoneChart.propTypes = {
handleZoneChange: PropTypes.func
};

export default GeoZoneChart;

EDIT 4:

I want to achieve that a code were executed after doDrilldown() . My problem is that when I do a drilldown on a point ( point.doDrilldown() ) the code loads a map async but the code continue executing (the map is not loaded yet) and fails (if I do not use a setTimeout ). So I need wait that doDrilldown() ends, load async map ends, and then, continue executing the code.

The code of @WojciechChmiel (modified, I have added async load but it is not working) I have been trying for achieving that is:

// @WojciechChmiel function modified (function(H) { H.Point.prototype.doDrilldown = function( _holdRedraw, category, originalEvent ) { var series = this.series, chart = series.chart, drilldown = chart.options.drilldown, i = (drilldown.series || []).length, seriesOptions;

  if (!chart.ddDupes) {
    chart.ddDupes = [];
  }

  while (i-- && !seriesOptions) {
    if (
      drilldown.series[i].id === this.drilldown &&
      chart.ddDupes.indexOf(this.drilldown) === -1
    ) {
      seriesOptions = drilldown.series[i];
      chart.ddDupes.push(this.drilldown);
    }
  }

  // Fire the event. If seriesOptions is undefined, the implementer can check
  // for  seriesOptions, and call addSeriesAsDrilldown async if necessary.
  H.fireEvent(chart, 'drilldown', {
    point: this,
    seriesOptions: seriesOptions,
    category: category,
    originalEvent: originalEvent,
    points: (
      category !== undefined &&
      this.series.xAxis.getDDPoints(category).slice(0)
    )
  }, function(e) {
    var chart = e.point.series && e.point.series.chart,
      seriesOptions = e.seriesOptions;

    if (chart && seriesOptions) {
      if (_holdRedraw) {
        chart.addSingleSeriesAsDrilldown(e.point, seriesOptions);
      } else {
        chart.addSeriesAsDrilldown(e.point, seriesOptions);
      }
    }
    // My code should go here?
    else {
      console.log(e.point)
      // Import all map data
      import(`./maps/${e.point['hc-key']}-all.geo`)
      .then(mapData => {

          // Set data of the map
          var data = [];
          mapData.default.features.forEach((element, i) => {
          data.push({ 'hc-key': element.properties['hc-key'], 'name': element.properties.name, 'value': 0 });
          });

          // Create the new drilldown serie
          try {
              var drilldownSerie = {
                  id: e.point['hc-key'],
                  mapData: mapData.default,
                  data: data,
                  joinBy: 'hc-key',
                  name: mapData.default.title,
                  allowPointSelect: true,
                  borderColor: '#ffffff',
                  borderWidth: 1.2,
                  states: {
                  hover: {
                      color: this.props.geoColor
                  },
                  select: {
                      color: this.props.geoColor
                  }
                  },
                  dataLabels: {
                  enabled: true,
                  format: '{point.name}'
                  },
                  point: {
                  events: {
                      click: (event) => {
                      this.props.handleZoneChange(event.point);

                      this.setState({
                          selectedPoint: event.point['hc-key']
                      });
                      }
                  }
              }
          };

          // Add the new drilldown serie
          this.internalChart.addSeriesAsDrilldown(e.point, drilldownSerie);

          // Select all map 
          //this.selectAll();
          } catch (err) {
          console.log(err.message)
          }
      }).catch((err) => {
          console.log(err.message)
      })
    }
  });

      console.log('After drilldown');
}
})(Highcharts);

EDIT 5:

This is the main problem:

this.internalChart.series[0].data.forEach(data => { // FIND A POINT IN ALL MAP
    if (data.name === SelectedName1) { // IF A POINT IS FOUND...
        data.doDrilldown() // DRILLDOWN TO CHILD MAP
        // HERE I MUST WAIT. WHEN DODRILLDOWN ENDS, CONTINUE WITH BELOW CODE
        // UNDERSTANDING "ENDS" WHEN NEW MAP AND ITS DATA IS LOADED AND READY
        this.internalChart.options.drilldown.series[0].data.forEach(dataInside => { // FOR EACH POINT OF NEW MAP (DRILLDOWN/CHILD MAP)
            if (dataInside.name === SelectedName2) { // IF A POINT IS FOUND
                this.setState({ province: dataInside['hc-key'] }); // CHANGE STATE
            }
        });
    }
});

Here is the wrapper of H.Point.prototype.doDrilldown :

(function(H) {
  H.Point.prototype.doDrilldown = function(
    _holdRedraw,
    category,
    originalEvent
  ) {
    var series = this.series,
      chart = series.chart,
      drilldown = chart.options.drilldown,
      i = (drilldown.series || []).length,
      seriesOptions;

    if (!chart.ddDupes) {
      chart.ddDupes = [];
    }

    while (i-- && !seriesOptions) {
      if (
        drilldown.series[i].id === this.drilldown &&
        chart.ddDupes.indexOf(this.drilldown) === -1
      ) {
        seriesOptions = drilldown.series[i];
        chart.ddDupes.push(this.drilldown);
      }
    }

    // Fire the event. If seriesOptions is undefined, the implementer can check
    // for  seriesOptions, and call addSeriesAsDrilldown async if necessary.
    H.fireEvent(chart, 'drilldown', {
      point: this,
      seriesOptions: seriesOptions,
      category: category,
      originalEvent: originalEvent,
      points: (
        category !== undefined &&
        this.series.xAxis.getDDPoints(category).slice(0)
      )
    }, function(e) {
      var chart = e.point.series && e.point.series.chart,
        seriesOptions = e.seriesOptions;

      if (chart && seriesOptions) {
        if (_holdRedraw) {
          chart.addSingleSeriesAsDrilldown(e.point, seriesOptions);
        } else {
          chart.addSeriesAsDrilldown(e.point, seriesOptions);
        }
      }
    });

        console.log('After drilldown');
  }
})(Highcharts);

As you can see this function is synchronous. If you use it to add data asynchronously please add an example that reproduces it.

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