-2

I have a function that is called twice with different parameters:

function setArray(chartType, charData, nodeName, meaQuantityId, unitName) {
  var legendText = nodeName + '(' + unitName + ')';
  var lineData = null;
  var scatterData = null;
  if (chartType != null) {
    switch (chartType) {
      case 'line':
        {
          if (compareGraphData.length > 0) {
            for (var i = 0; i < compareGraphData.length; i++) {
              if (legendText == compareGraphData[i].legendText) {
                legendText = changeLegendText(legendText, i);
              }
            }
          }
          lineData = {
            type: chartType, //try changing to column, area
            showInLegend: true,
            legendText: legendText,
            toolTipContent: "{label}LS : {y} ",
            dataPoints: charData
          };
          compareGraphData.push(lineData);
          break;
        }
      case 'scatter':
        {
          if (compareScatterGraphData.length > 0) {
            for (var i = 0; i < compareScatterGraphData.length; i++) {
              if (legendText == compareScatterGraphData[i].legendText) {
                //legendText = changeLegendText(legendText, i);
              }
            }
          }
          scatterData = {
            type: chartType, //try changing to column, area
            showInLegend: true,
            legendText: legendText,
            toolTipContent: "{label}LS : {y} ",
            dataPoints: charData
          };
          compareScatterGraphData.push(scatterData);
          break;
        }
    }
  } else {
    var arr = [];
    for (var i = 0; i < charData.length; i++) {
      arr.push({
        'quantityName': charData[0].nodeName,
        'data': charData[i].value,
        'count': charData[0].count,
        'unitName': charData[0].xUnitName,
        'meaQuantityId': meaQuantIdForDataTable
      });
    }
    dataView.push(arr);
  }
}

function changeLegendText(legendText, i) {
  var value = webix.ui({
    view: "window",
    modal: true,
    id: 'id-win-change-channel-Name',
    fullscreen: true,
    position: function(state) {
      state.width = 220;
      state.top = 200;
      state.left = (window.innerWidth - state.width) / 2;
      state.height = 150;
    },

    head: "Change Alias!",
    body: {
      padding: 10,
      rows: [{
        view: 'text',
        id: "id-text-change-channel-Name",
        value: legendText + '_' + i
      }, {
        view: 'button',
        value: "OK",
        click: function() {
          channelAliasValue = $$('id-text-change-channel-Name').getValue();

          $$('id-win-change-channel-Name').hide();
        }
      }]
    }
  }).show();
}

////I call the function like this:

DBService.getGraphData('getGraphData')
    .then(function(response){
    var charData=response.result;
    setArray('line', charData, nodeName, meaQuantityId, unitName);
    setArray('line', charData, nodeName, meaQuantityId, unitName);
});
I have compared legendText with the one in the compareGraphData array. If it is true then a popup comes up with a text box and a button.

I want to stop the execution when the popup comes up and continue the execution after the user clicks on the button.

But the above code doesn't wait for the user event and continue with the next statement.

Can you tell me the best possible way to achieve this?

Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100
Ankur
  • 33
  • 1
  • 1
  • 6
  • 2
    What do you mean? `As javascript is asynchronous` Javascript is synchronous unless you make it otherwise –  Dec 26 '17 at 16:28
  • 3
    "As javascript is asynchronous" — It isn't. External APIs that JS accesses can be. How you interact with them to determine when they have finished depends on the API. You haven't told us what API you are interacting with. – Quentin Dec 26 '17 at 16:29
  • 1
    Look into promises ... https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises – Daniel Dec 26 '17 at 16:30
  • Put some actual working code in there that demonstrates your challenge and perhaps we can assist you in fixing that. – Mark Schultheiss Dec 26 '17 at 18:41
  • @MarkSchultheiss I have updated the post and put the actual code as per your request. – Ankur Dec 27 '17 at 06:30
  • where is `compareGraphData`? Likely something used in that function should be passed to that function to make it more encapsulated and easier to maintain and understand – Mark Schultheiss Dec 27 '17 at 14:52
  • "I have a function that is called twice with different parameters:" Really? As you have it, it calls it twice true but with the same exact parameters. – Mark Schultheiss Dec 27 '17 at 14:57
  • @MarkSchultheiss Sorry, there's a typo in the second `setArray()` function call. The second call must be like this: `setArray('scatter', charData, nodeName, meaQuantityId, unitName)`. – Ankur Dec 28 '17 at 07:07

3 Answers3

0

You can pass a function to your function like this:

function setArray(chartType, data, cb){
 cb("hello")
} 

setArray('scatter', data, function(x){
 console.log(x) // hello
});

Double

function setArray(chartType, data, cb){
 cb(data)
} 

setArray('scatter', "Hello ", function(x){
 setArray('scatter', "World!", function(y){
    console.log(x + y)// Hello world!
 })
});

or use async

Jonathan Dion
  • 1,651
  • 11
  • 16
  • I think the OP was looking for a promise that only resolves the latest one. For example: user clicks on line, takes 1 second to resolve but during that time user clicks on scatter that takes 100ms to resolve. You now are in trouble because data what the user actually wants to see comes in before data that the user previously wanted to see. – HMR Dec 26 '17 at 17:44
0

In case of asynchronous execution (which is not clear with the missing setArray function body), this can be resolved with a promises. Reference here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

let setArray = function setArray('line', data) {
  return new Promise((resolve, reject) => {
    // logic for function setArray here
  });
};

let call1 = setArray('line', data).then(function(result) {
  // do stuff with result
});

let call2 = setArray('scatter', data).then(function(result) {
   // do stuff with result
});

However, if the function body of setArray has no asynchronous parts, the regular function call can be executed without problems. If it does not work, there is likely another problem with the code.

Daniel
  • 4,816
  • 3
  • 27
  • 31
  • I think that's only part of the problem, the user may change it's mind while promise 1 is resolving thus creating promise 2. When promise 2 resolves before promise 1 and you'll just blindly update the ui according to the resolve value then the ui would display result of promise 2 and then result of promise 1. The user is now looking at something that it didn't choose. You also get flickering UI when you just blindly update UI based on data coming in. – HMR Dec 26 '17 at 17:42
  • 1
    @Ankur - You mention problems with asynchronicity, but your source code is lacking information to be all exclusive in solving this problem. And HMR has a point if you want to admit a new call and break of the previous one, which seems to be what you want. – Daniel Dec 26 '17 at 17:53
  • @Ankur You should try to communicate a bit better. `Not working` is the worst a programmer can hear and should never say to another programmer. Instead you should define your expected behavior, actual behavior and if there is an error provide the error and where you get it. `Not working` doesn't do anything to solve your problem, instead update your question with what you tried and then comment Daniel. – HMR Dec 26 '17 at 17:54
  • 1
    @HMR - Good point. It is not clear in OP question if it is a replacement call. – Daniel Dec 26 '17 at 18:03
  • 1
    @HMR I have updated my post as per your request, and I tried to explain my problem in detail. – Ankur Dec 27 '17 at 05:51
  • @HMR charData is the response that comes from AngularJS service from the backend. I have updated the post for the same. – Ankur Dec 27 '17 at 10:34
  • @HMR setArray is called twice with different parameters. And getGraphData is started on drop event of a tree node by a user. – Ankur Dec 27 '17 at 11:10
  • @Ankur Updated my answer as how to implement resolving only last promise in your code. I think you need to look at your functions and split them into smaller functions that don't implement so much. Also try not to use magic values, your functions seem to be mutating variables that are not defined in the function nor passed to it (like `compareGraphData`) – HMR Dec 27 '17 at 11:50
0

I think what you want is to only use the last change.

For example the user has a list of all animals and can choose to show only slow or fast animals but the filter is asynchronous.

User clicks to show only slow animals and it'll take 1 second for the filter to return data. Within this one second user chooses to show all animals so the user interface is set on showing all animals. Showing all takes 100 milliseconds, returns and updates the list to show all. Then the slow animals list comes in and list is showing only slow animals.

Now the user interface and the list presented are out of sync. This problem is described here.

A solution to this problem I implemented was to pass a promise to a function that will only resolve the promise if it was the last created promise (most recent user action).

How to convert a function that uses callbacks to a function that returns a promise can be found here (under: Converting callback to promise.)

[UPDATE]

As stated yesterday; I think the user changes something and then changes something else so 2 promises are getting data that do not resolve at the same time the user created them and/or are both resolving so your application is doing something that is no longer needed.

The answer I gave yesterday can be applied to your code in the following way using onlyLastPromise from lib:

const onlyLastRequestedPromise = ((promiseIds) => {
  const whenResolve = (promise, id, promiseID, resolveValue) => {
    if(promise!==undefined){//called by user adding a promise
      promiseIds[id]={};
    } else {//called because promise is resolved
      return (promiseID === promiseIds[id])
        ? Promise.resolve(resolveValue)
        : Promise.reject("A newer request was made.")
    }
    return (function (currentPromiseID) {
      return promise
        .then(function (result) {
          return whenResolve(undefined,id, currentPromiseID, result);
        });
    }(promiseIds[id]));
  };
  return (id = "general") => promise =>
    whenResolve(promise, id);
})({});
//this is for your fetching your data, add it to your service
const lastForChart=onlyLastRequestedPromise("CHART_DATA");

//wrap your getGraphData in a "only last"
lastForChart(DBService.getGraphData('getGraphData'))
.then(
  function(response){
    var charData=response.result;
    setArray('line', charData, nodeName, meaQuantityId, unitName);
    setArray('line', charData, nodeName, meaQuantityId, unitName);
})
.catch(
  err=>
    (err==="A newer request was made.")
      ? "replaced by newer request"
      : Promise.reject(err)
);
HMR
  • 37,593
  • 24
  • 91
  • 160
  • 1
    Agree, downvotes should be accompanied with a comment .. otherwise it's pointless in helping OP ... I voted you back up, because it is a good point you have. OP is not quite clear. – Daniel Dec 26 '17 at 18:04