0

Within a timer called javascript function I need update a UI using several json API calls. It's an energy meter which stores the actual consumed phase power in 3 different registers. Except for negative values (energy by solar panels), this returned energy is stored in 3 other registers (as a positive value). If phase power is 0, I need need to fetch the alternate phase power register.

Loop 3 times
  Fetch A at api1
  If A=0 Then
    Fetch A at api2
  Display A
  Display min(A)
  Display max(A)
  Display trend
  Even more display stuff I don't want to duplicate

I manage to fetch the value, however can't get the alternative fetched value to survive below the fetch block. Display A = 0 when fetching api2 (which returns not 0).

I tried async and await, like Javascript - How to use fetch inside a for loop and wait for results. Thus change 2 lines in the code below:

async function update()
…
                        await fetch(APIGW+"v1/sm/fields/power_returned_l"+myPhase)

That results in:

Uncaught SyntaxError: await is only valid in async functions and async generators
Uncaught ReferenceError: update is not defined

How to make the bottom document.getElementById (and loads of UI stuff below) to wait for the 2nd level (not the 2nd loop) fetch when this if (nvKW == 0 ) block is executed?

function update()
{
    var phase;
    for( let phase = 1 ; phase <= PHASES ; phase++ )
    {
        fetch(APIGW+"v1/sm/fields/power_delivered_l"+phase)
        .then(response => response.json())
        .then(json => 
          {
            for( let j in json.fields ){
                if (json.fields[j].name.startsWith("power_delivered_l"))
                {
                    myPhase = Number(json.fields[j].name.replace('power_delivered_l',''));

                    let nvKW=Number(json.fields[j].value)
                    if (nvKW == 0 ) // check if power is generated
                    {
                        fetch(APIGW+"v1/sm/fields/power_returned_l"+myPhase)
                        .then(response => response.json())
                        .then(json2 => 
                          {
                            for( let jr in json2.fields ){
                                if (json2.fields[jr].name.startsWith("power_returned_l"))
                                {
                                    let nvKW=-1*Number(json2.fields[jr].value)
                                    console.log(json2.fields[jr].name+" = "+ nvKW.toString()) // here nvKW contains a value
                                }
                            }
                          }
                        );
                    }

                    console.log("nvKW = "+ nvKW.toString()) // 1st level fetch = 0 and 2nd level fetch != 0 then nvKW is still 0 here, where I need the value from the 2nd level fetch
                    document.getElementById(json.fields[j].name).innerHTML = nvKW.toFixed(1);
                    // a lot more ui stuff is done below
Pro Backup
  • 729
  • 14
  • 34
  • Then... you can put `document.getElementById(json.fields[j].name).innerHTML = nvKW.toFixed(1);` inside the `.then(json2 => {})` code block – Pipe Apr 22 '21 at 20:48
  • @Pipe I can't put `document.getElementById(json.fields[j].name).innerHTML = nvKW.toFixed(1);` inside the `.then(json2 => {})` because all the UI stuff should also get executed when `nvKW != 0 `. In such a case that whole `if(){}` code block is skipped over. – Pro Backup Apr 22 '21 at 21:30
  • 1
    Using `await` for the second `fetch` (inside the `if` block) would work, can you show us *how* you tried to use it? Alternatively, see [if-else flow in promise](https://stackoverflow.com/a/26600424/1048572). Notice the outer (first) `fetch` and the loop around it are irrelevant to this problem. – Bergi Apr 22 '21 at 23:08
  • @Bergi I have inserted my `async` `await` try and the errrors in return, in the question above. – Pro Backup Apr 23 '21 at 06:51

2 Answers2

1

Using .then() syntax on a conditional promise:

json => {
    const field = getFieldByName(json, "power_delivered_l");
    const myPhase = field.name.replace('power_delivered_l','');
    (field.value == 0 // check if power is generated
      ? fetch(APIGW+"v1/sm/fields/power_returned_l"+myPhase)
        .then(response => response.json())
        .then(json2 => getFieldByName(json2, "power_returned_l", -1))
      : Promise.resolve(field)
    ).then(({name, value: nvKW}) => {
         console.log(`${name} = ${nvKW}`)
         document.getElementById(json.fields[j].name).innerHTML = nvKW.toFixed(1);
         // a lot more ui stuff is done below
    });
}

Using async/await:

async json => {
    let field = getFieldByName(json, "power_delivered_l");
    if (field.value == 0) // check if power is generated
        const myPhase = field.name.replace('power_delivered_l','');
        const response = await fetch(APIGW+"v1/sm/fields/power_returned_l"+myPhase);
        const json2 = await response.json())
        field = getFieldByName(json2, "power_returned_l", -1))
    }
    const {name, value: nvKW} = field;
     console.log(`${name} = ${nvKW}`)
     document.getElementById(json.fields[j].name).innerHTML = nvKW.toFixed(1);
     // a lot more ui stuff is done below
}

Both using a helper function

function getFieldByName(json, prefix, factor = 1) {
    for (const field of json.fields) {
        if (field.name.startsWith(prefix)) {
             return {
                 name: field.name,
                 value: factor * Number(field.value)
             };
        }
    }
    throw new Error(`Did not find field '${prefix}' in the JSON`);
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I opted for the `.then()` construct. During daytime (with negative power values) this runs ok. Still needs testing during night time. – Pro Backup Apr 23 '21 at 12:35
0

Then use an else:

function updateUIwithA(A){
   //Do whatever you need to update UI from A
   //document.getElementById("fieldNameForDisplayAValue").innerHTML = getValue(A);
   //document.getElementById("fieldNameForDisplayAMin").innerHTML = getMin(A);
   //document.getElementById("fieldNameForDisplayAMax").innerHTML = getMax(A);
   //document.getElementById("fieldNameForDisplayAverage").innerHTML = getAverage(A);
}


                    ...
                    let nvKW=Number(json.fields[j].value)
                    if (nvKW == 0 ) // check if power is generated
                    {
                        //First power is 0, then fetch the second power
                        fetch(APIGW+"v1/sm/fields/power_returned_l"+myPhase)
                        .then(response => response.json())
                        .then(json2 => 
                          {
                            for( let jr in json2.fields ){
                                if (json2.fields[jr].name.startsWith("power_returned_l"))
                                {
                                    //Show second power
                                    let nvKW2=-1*Number(json2.fields[jr].value)
                                    console.log(json2.fields[jr].name+" = "+ nvKW.toString()) // here nvKW contains a value
                                    updateUIwithA(json2);
                                }
                            }
                          }
                        );
                    } else {
                       //First power is not 0, so show the current value and ignore second fetch
                       updateUIwithA(json);
                    }

Pipe
  • 2,379
  • 2
  • 19
  • 33
  • Beside displaying the value also min/max/etcetera has to be checked and displayed. In such a case all UI code needs duplication. Maybe I can wrap it in a function. Still less ideal. – Pro Backup Apr 23 '21 at 06:27
  • Then... in your pseudo code, you only have values called A... in my example, A is json or a is json2 (when the first one is equal to 0)... Then you can do a function to update All your UI with that value.. no need to repeat code... I will update my code. – Pipe Apr 23 '21 at 12:40
  • Pipe thank you for your effort. I still prefer the approach from Bergi. – Pro Backup Apr 23 '21 at 16:50