P4JSAPI to P4V-JS Conversion Guide (2020.2)

Port P4JsApi code

Let’s start with a simple P4JSAPI program. This program runs p4 info and displays the ‘set of name/value pairs’ result in an HTML table:

---- jsapi/serverinfo.html
<html>
  <head>
    <meta http-equiv="Content-Type"
          content="text/html; charset=utf-8" />
    <title>Server Info</title>
    <style>
      body {
      font:normal 12px serif;
      }
      .nameFont {
      color:DarkGray;
      }
    </style>
    <script type="text/javascript">
      function runOnload() {
        try {
          var serverName = P4JsApi.getPort();
          
          // Run the p4 info command.
          var serverInfo =
            P4JsApi.p4(' -p ' + serverName + ' info');

          // Populate server info into table
          var serverInfoTable =
            document.getElementById("serverInfoTable");
          var index = 0;
          for (var key in serverInfo.data[0]) {
            var row = serverInfoTable.insertRow(index++);
 
            // left cell
            var cellLeft = row.insertCell(0);
            var textNode = document.createTextNode(key);
            cellLeft.appendChild(textNode);
            cellLeft.setAttribute("class", "nameFont");
       
            // right cell
            var cellRight = row.insertCell(1);
            textNode = document.createTextNode(
            serverInfo.data[0][key]);
            cellRight.appendChild(textNode);
          }
        } catch(e) {
          alert(e);
        }
      }
    </script>
  </head>
  <body onload="runOnload();">
    <table id="serverInfoTable"></table>
  </body>
</html>
---- end of - jsapi/serverinfo.html

This is a synchronous implementation. P4JsApi.p4(' -p ' + serverName + ' info') waits for the result to come back and assigns it to var serverInfo. The code then continues loading the serverInfoTable.

The P4VJS implementation looks slightly different:

---- p4vjs/serverinfo1.html
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=utf-8" /> <title>Server Info</title>
<style>
body {
font:normal 12px serif;
}
</style>
<script src="./p4vjs.js"></script>
<script type="text/javascript">
async function runOnload() {
try {
var serverInfo = await p4vjs.p4('info');
var serverInfoTable = document.getElementById("serverInfoTable");
var index = 0;
for (var key in serverInfo.data[0]) {
var row = serverInfoTable.insertRow(index++);
// left cell
var cellLeft = row.insertCell(0);
var textNode = document.createTextNode(key);
cellLeft.appendChild(textNode);
cellLeft.setAttribute("class", "nameFont");
// right cell
var cellRight = row.insertCell(1);
textNode = document.createTextNode(
serverInfo.data[0][key]);
cellRight.appendChild(textNode);
}
} catch(e) {
alert(e);
}
}
</script>
</head>
<body onload="runOnload();">
<table id="serverInfoTable"></table>
</body>
</html>
---- end of – p4vjs/serverinfo1.html

P4VJS commands are implemented in <script src="./p4vjs.js"></script>. This file needs to be included to support P4VJS commands.

P4VJS commands all implement a JavaScript Promise. To get synchronous behavior using a Promise, you need to use a an async/await pair. To allow a function to be marked as await, the calling function has to be marked as async.

async function runOnload() { … }

Marking a function as async has the following effects:

  • The function now returns a Promise (you changed its return value).
  • The function allows synchronous method calling (you can use await in the function).

await calls p4vjs.p4(…) and waits for the result:

var serverInfo = await p4vjs.p4('info');

Changing the same program to an asynchronous implementation looks like this:

---- p4vjs/serverinfo2.html
<html>
  <head>
    <meta http-equiv="Content-Type"
      content="text/html; charset=utf-8" />
    <title>Server Info</title>
    <style>
      body {
      font:normal 16px verdana;
      }
    </style>
    <script src="./p4vjs.js"></script>
    <script type="text/javascript">
      function runOnload() {
        try {
          // Populate server info into table
          var serverInfoTable =
            document.getElementById("serverInfoTable");
          p4vjs.p4('info').then(function(serverInfo) {
            var index = 0;
            for (var key in serverInfo.data[0]) {
                 var row = serverInfoTable.insertRow(index++);

                 // left cell
                 var cellLeft = row.insertCell(0);
                 var textNode = document.createTextNode(key);
                 cellLeft.appendChild(textNode);

                 // right cell
                 var cellRight = row.insertCell(1);
                 textNode = document.createTextNode(
                   serverInfo.data[0][key]);
                 cellRight.appendChild(textNode);
            }
          });
        } catch(e) {
            alert(e);
        }
      }
    </script>
  </head>
  <body onload="runOnload();">
    <table id="serverInfoTable"></table>
  </body>
</html>
---- end of – p4vjs/serverinfo2.html

Since p4vjs.p4('info') is implemented as a Promise, the .then() operator can be called to chain the result of the Promise to an inline function. This implementation does not wait; runOnLoad(..) returns before the response of p4vjs.p4('info') has been received. The inline .then(function(serverInfo) {..} is called when the P4VJS function returns a value.

The syntax:

p4vjs.p4('info').then(function(serverInfo) { … }

p4vjs.p4(…, …) takes a third parameter that can be used to pass in a named callback function instead of an anonymous inline function.

---- p4vjs/serverinfo3.html
<html>
  <head>
    <meta http-equiv="Content-Type"
          content="text/html; charset=utf-8" />
    <title>Server Info</title>
    <style>
      body {
      font:normal 16px verdana;
      }
      .nameFont {
      font-weight:bold;
      color:DarkGray;
      }
    </style>
    <script src="./p4vjs.js"></script>
    <script type="text/javascript">
      function runOnload() {
        try {
            // Populate server info into table
            var serverInfoTable =
              document.getElementById("serverInfoTable");
            var loadcallback = loadData;
            p4vjs.p4('info', '', loadcallback);
        } catch(e) {
            alert(e);
        }
      }
      function loadData(serverInfo) {
        console.log("p4 info called", serverInfo);
        var index = 0;
        for (var key in serverInfo.data[0]) {
             var row = serverInfoTable.insertRow(index++);
  
             // left cell
             var cellLeft = row.insertCell(0);
             var textNode = document.createTextNode(key);
             cellLeft.appendChild(textNode);
             cellLeft.setAttribute("class", "nameFont");

             // right cell
             var cellRight = row.insertCell(1);
             textNode = document.createTextNode(
               serverInfo.data[0][key]);
             cellRight.appendChild(textNode);
      }
    }
    </script>
  </head>
  <body onload="runOnload();">
    <table id="serverInfoTable"></table>
  </body>
</html>
|---- end of – p4vjs/serverinfo3.html

The syntax:

var loadcallback = loadData;
p4vjs.p4('info', '', loadcallback);

The third parameter has to be a variable that references a function. p4vjs.p4(…) is the only function that supports a callback parameter.