简体   繁体   中英

Stop custom function from auto refreshing/periodically calling external API

I am using Google Apps Script and a custom function to call an external API to verify phone numbers.

Below is the code for my function.

/**
 * This CUSTOM FUNCTION uses the numVerify API to validate 
 * a phone number based on the input from JotForm and a 
 * country code which is derived from the JotForm country
 *
 * Numverify website: https://numverify.com/dashboard (account via LastPass)
 * Numverify docs: https://numverify.com/documentation
 */
function PHONE_CHECK(number, country){
  
  if(country == "")
    return [["", "country_not_set"]]
  
  // check the API result has already been retrieved
  var range = SpreadsheetApp.getActiveSheet().getActiveRange()
  var apires = range.offset(0, 1).getValue()
  if(apires.length > 0)
    return range.offset(0, 0, 1, 2).getValues()
    
    
  var url = 'http://apilayer.net/api/validate'
  + '?access_key=' + NUMVERIFY_KEY
  + '&number=' + encodeURIComponent(number)
  + '&country_code=' + encodeURIComponent(country)
  + '&format=1';

  var response = UrlFetchApp.fetch(url, {'muteHttpExceptions': true});
  var json = response.getContentText();
  var data = JSON.parse(json);
  
  if(data.valid !== undefined){
    if(data.valid){
      return [[data.international_format, "OK"]]
    }else{
      return [["", "invalid_number"]] // overflows data to the next column (API Error) while keeping the phone field clear for import into TL
    }
  }else if(data.success !== undefined){
    if(data.error.type.length > 0){
      return [[number, data.error.type]]
    }else{
      return [[number, "no_error_type"]]
    }
  }else{
    return [[number, "unexpected_error"]] // this generally shouldn't happen...
  }
}

Given this formula, which takes a phone number and country code, it will then check the phone number against the numverify API and return the result in the cell and overflow to the cell to the right of it. The overflow is used to indicate whether the API was called successfully and to check if the result was already retrieved .

Example: =PHONE_CHECK("+32123456789", "BE")

Note that the first cell is empty because the API returns an 'invalid phone number' code. Because of privacy, I won't put any real phone numbers here. In case I would've used a real phone number, the first cell would contain the phone number formatted in the international number format.

示例自定义函数输出

Since I'm using the free plan, I don't want to rerun the function every time if I already know what the result is , as I don't want to run up against the rate limit. Unfortunately, this doesn't seem to work and periodically (it looks like once every day), it will refresh the results for each row in the sheet.

So two questions:

  1. Is something wrong with my logic in checking the API result and then just exiting the function? (see below for the code)
  2. If the logic is right, why does Google Sheets seem to periodically ignore (or refresh?) the values in that second column and call the external API anyhow?
  var range = SpreadsheetApp.getActiveSheet().getActiveRange() // get the cell from which the function is called
  var apires = range.offset(0, 1).getValue() // get the values directly to the right of the cell
  if(apires.length > 0) // check if there's anything there...
    return range.offset(0, 0, 1, 2).getValues() // return an array that basically just resets the same values, effectively stopping the script from running

Your Aim:

You want a custom function, AKA a formula to only run once, or as many times as is necessary to produce a certain result. You want the same formula to write a value to the another cell, for example the adjacent cell, that will tell the formula in future, if it should be run again or not.

Short Answer:

I'm afraid that values that are evaluated from custom functions AKA formulas are transient, and what you want to accomplish is not possible with them.

Explanation:

You can run a quick test with this custom function:

function arrayTest() {
  return [[1, 2, 3, 4 ,5]]
}

If you put this in a cell as below:

arrayTest 公式演示

You will see that if you delete the formula in the original cell, the overflow values also dissapear.

Therefore something like the following code will almost always produce the same value:

function checkTest() {
  var cell = SpreadsheetApp.getActiveRange()
  var status = cell.offset(0, 1).getValue();
  
  if (status != "") {
    return "already executed" // in your case without calling API
  } else {
    return [["OK","executed"]] // in your case making API call - will happen ~90% of the time.
  }
}

// OUTPUT [["OK","executed"]]

Here I am inserting a row and deleting it to force re-calculation of the formulas.

checkTest() 演示

The first thing that Sheets does before re-calculating a formula is that it clears the previous values populated by formula. Since the conditional statment depends on the value of its previous execution, it will always evaluate to the same result. In your case, it will almost always make the API call .

Confusingly, this is not 100% reliable! You will find that sometimes , it will work as you intend. Though in my tests, this only happened around 1 times out of 10, and most often when the formulas updated when saving changes to the script editor.

Ideally, though not possible, you would want to be able to write something like this:

function checkTest() {
  var cell = SpreadsheetApp.getActiveRange();
  var cellValue = cell.getValue();
  var adjacentCell = cell.offset(0, 1);
  var status = adjacentCell.getValue();
  
  if (status == "") {
    cell.setValue(cellValue)
    adjacentCell.setValue("executed")
  }
}

Which would clear the formula once it has run, alas, setValue() is disabled for formulas ! If you wanted to use setValue() you would need to run your script from a menu, trigger or the script editor. In which case it would no longer make sense as a formula.z

References

https://developers.google.com/apps-script/guides/sheets/functions

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