Use async/await For Asynchronous Processing

Contents

Overview

This article introduces how to use async/await instead of Promise to simplify asynchronous processing in Kintone customizations.

What Is async/await?

async/await is a syntax that executes asynchronous operations, such as API calls, in order from top to bottom. It enables retrieving data, storing values in variables, and waiting for processes to complete.
Practical examples are introduced later in this article.

async/await is built on Promise objects and can be considered a more convenient way to write Promise-based code.

Issues With Promise

Promise has the following drawbacks:

  • More difficult to understand than regular code
    Promise is a mechanism for handling asynchronous processing, but it can be harder to understand than regular code.
    Using methods like then() and catch() can make the code flow less clear.
  • Sequential asynchronous processing requires multiple then() calls and becomes harder to read
    With Promise, chaining asynchronous operations requires connecting multiple then() calls.
    This can lead to deep nesting and reduced readability.

Due to JavaScript specifications, synchronous waiting for responses is not possible in asynchronous operations such as API calls.
For this reason, mechanisms like Promise are needed to handle asynchronous results.

The following is an example of retrieving three records in series using Promise.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
kintone.events.on(['app.record.create.submit', 'app.record.edit.submit'], (event) => {
  return new kintone.Promise((resolve, reject) => {
    return kintone.api('/k/v1/record.json', 'GET', params1).then((resp1) => {
      // The response is stored in resp1. Wait for completion before moving to the next step.
      console.log(resp1);
      return kintone.api('/k/v1/record.json', 'GET', params2);
    }).then((resp2) => { // The response is stored in resp2. Wait for completion before moving to the next step.
      console.log(resp2);
      return kintone.api('/k/v1/record.json', 'GET', params3);
    }).then((resp3) => { // The response is stored in resp3. Wait for completion before moving to the next step.
      console.log(resp3);
      resolve(event); // Call resolve to signal that the Promise has completed
    });
  });
});

Benefits of async/await

Using async/await provides the following benefits:

  • More concise and intuitive than Promise-based asynchronous code.
  • No need to chain multiple then() calls when running asynchronous operations in sequence.
  • Based on Promise, so methods like .then() and .all() remain available.

The following compares code written with Promise to code written with async/await.

  • Retrieve a single record when the Record Details page is opened using standard Promise.

    1
    2
    3
    4
    5
    
    kintone.events.on('app.record.detail.show', () => {
      return kintone.api('/k/v1/record.json', 'GET', {app: 1, id: 1}).then((resp) => {
        console.log(resp); // Display the retrieved content
      });
    });
  • Retrieve a single record when the Record Details page is opened using async/await.

    1
    2
    3
    4
    
    kintone.events.on('app.record.detail.show', async () => {
      const resp = await kintone.api('/k/v1/record.json', 'GET', {app: 1, id: 1});
      console.log(resp); // Display the retrieved content
    });

This syntax stores the API response in the resp variable, just like a regular variable declaration.

When using await, prefix the enclosing function with async to indicate it is an async function.

An async function returns a Promise.
This allows Kintone to wait for the result.

How to Use async/await

Declare async at the beginning of the function, and declare await at each location where the process should wait.
Note that forgetting to add async is a common mistake.
Actual usage in Kintone is demonstrated in the examples below.

1
2
3
async () => {
  await asyncProcess
}

MDN also provides examples for reference.
MDN Web Docs: async function (External link)

Examples

The following sections show additional specific examples.
For comparison, both Promise-based and async/await-based versions are provided.

Example 1: Set Default Values From Another Record on the Record Edit Page

Example using Promise
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
kintone.events.on('app.record.edit.show', (event) => {
  const params = {app: 1, id: 1}; // Specify any parameters as needed

  // kintone.api() returns a Promise object when the 4th argument is omitted, so return it directly.
  // Chain with .then() to wait for processing
  return kintone.api('/k/v1/record.json', 'GET', params)
    .then((resp) => {
      // Overwrite the field with code Text_1 with the value retrieved from the API
      event.record.Text_1.value = resp.record.Text_1.value;
      return event;
    }).catch((e) => {
      // Handle errors from API execution, such as incorrect parameters
      alert(e.message);
      return event;
    });
});
Example using async/await
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
kintone.events.on('app.record.edit.show', async (event) => {
  const params = {app: 1, id: 1}; // Specify any parameters as needed
  try {
    // kintone.api() returns a Promise object when the 4th argument is omitted, so await it
    // The API return value can be stored in a variable
    const resp = await kintone.api('/k/v1/record.json', 'GET', params);
    // Overwrite the field with code Text_1 with the value retrieved from the API
    event.record.Text_1.value = resp.record.Text_1.value;
    return event;
  } catch (e) {
    // Handle errors from API execution, such as incorrect parameters
    alert(e.message);
    return event;
  }
});

Use the try/catch syntax for error handling.

Refer to the following article for details on the errors returned by kintone.api():
Kintone REST API Overview - HTTP Status Codes

Example 2: Retrieve Three Records and Sum Their Values on Record Save

Example using Promise
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
kintone.events.on(['app.record.create.submit', 'app.record.edit.submit'], (event) => {

  // kintone.api() returns a Promise object when the 4th argument is omitted, so return it directly.
  // Chain with .then() to wait for processing
  // Note: this example uses sequential chaining, but Promise.all() could also be used in this case.
  return kintone.api('/k/v1/record.json', 'GET', params1)
    .then((resp1) => {
      event.record.Total.value += Number(resp1.record.Subtotal.value);
      // Second API call
      return kintone.api('/k/v1/record.json', 'GET', params2);
      // Chain with .then() to wait for processing
    }).then((resp2) => {
      event.record.Total.value += Number(resp2.record.Subtotal.value);
      // Third API call
      return kintone.api('/k/v1/record.json', 'GET', params3);
      // Chain with .then() to wait for processing
    }).then((resp3) => {
      event.record.Total.value += Number(resp3.record.Subtotal.value);
      return event;
    }).catch((e) => {
      // Handle errors from API execution, such as incorrect parameters
      alert(e.message);
      return event;
    });
});
Example using async/await

Chained operations written this way become much easier to follow.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
kintone.events.on(['app.record.create.submit', 'app.record.edit.submit'], async (event) => { // Add async
  try {
    //          ↓ Add await to the process that should be waited for
    const resp1 = await kintone.api('/k/v1/record.json', 'GET', params1);
    // The response is stored in resp1. Wait for completion before moving to the next step.
    const resp2 = await kintone.api('/k/v1/record.json', 'GET', params2);
    // The response is stored in resp2. Wait for completion before moving to the next step.
    const resp3 = await kintone.api('/k/v1/record.json', 'GET', params3);
    // The response is stored in resp3. Wait for completion before moving to the next step.

    // All data retrieval is complete at this point, so calculations can proceed
    // Sum resp1 through resp3 and set the result in the Total field
    event.record.Total.value = Number(resp1.record.Subtotal.value) + Number(resp2.record.Subtotal.value) + Number(resp3.record.Subtotal.value);
    return event;
  } catch (e) {
    // Handle errors from API execution, such as incorrect parameters
    alert(e.message);
    return event;
  }
});

When To Consider Promise

Using async/await does not eliminate the need to understand Promise. As noted above, async/await simply makes asynchronous code easier to write.
In some cases, using Promise may be more suitable than async/await. For example, when executing multiple asynchronous operations in parallel, using Promise.all() can be more efficient than awaiting each operation sequentially.

1
2
3
4
5
// Example mixing async/await with traditional Promise syntax

const resp2 = await kintone.api('/k/v1/record.json', 'GET', {app: 1, id: 1}).then((resp1) => {
  return kintone.api('/k/v1/record.json', 'GET', {/* Change the param based on resp1 */});
});

When writing code like the example above, unifying with async/await is recommended.

1
2
const resp1 = await kintone.api('/k/v1/record.json', 'GET', params1);
const resp2 = await kintone.api('/k/v1/record.json', 'GET', {/* Change the param based on resp1 */});

Promise and async/await have the following relationship:

  • await can be used to wait for a Promise to complete.
    As shown in the earlier examples, kintone.api() can be awaited.
    This is because kintone.api() returns a Promise object.
  • An async function returns a Promise.
    An async function returns a Promise object.
    This is why then and await can be combined, as shown above.
    However, mixing these styles excessively causes confusion and is generally discouraged.