async/await and recursion

Jean Lebrument picture Jean Lebrument · Nov 19, 2015 · Viewed 13.2k times · Source

I'm trying to code a method that show recursively an ActionSheetIOS to select a value contained from arrays and return the selected values:

async function _rescursiveSelect(data, index) {
  if (index < data.length) {
    const object = data[index];

    if (object.array.length === 1) {
      return await _rescursiveSelect(data, index + 1);
    }

    ActionSheetIOS.showActionSheetWithOptions({
      title: 'Choose a value from array: ',
      options: object.array,
    },
    buttonIndex => async function() {
      const selectedValue = data[index].array[buttonIndex];
      data[index].value = selectedValue;
      delete data[index].array;

      return await _rescursiveSelect(data, index + 1);
    });
  } else {
    return data;
  }
}

Unfortunately, when I call this method, it returns undefined. I guess the problem comes from async/await using but I didn't fount it yet.

Any suggestion?

Answer

Tamas Hegedus picture Tamas Hegedus · Nov 19, 2015

It returns undefined because there is a path where there is no return statement. The async-await pattern works well with async functions, however ActionSheetIOS.showActionSheetWithOptions is not async.

An async function is simply a function which returns a Promise. The async keyword is just a syntactic sugar to make async code readable, and hides the promise handling behind it.

Fortunately libraries that use old-style callback functions can be easily wrapped to new-style Promise-returning async functions, like this:

function showActionSheetWithOptionsAsync(options) {
    return new Promise(resolve => { 
        // resolve is a function, it can be supplied as callback parameter 
        ActionSheetIOS.showActionSheetWithOptions(options, resolve);
    });
}

async function _rescursiveSelect(data, index) {
    if (index < data.length) {
        const object = data[index];

        if (object.array.length === 1) {
            return await _rescursiveSelect(data, index + 1);
        }

        const buttonIndex = await showActionSheetWithOptionsAsync({
            title: 'Choose a value from array: ',
            options: object.array
        });
        const selectedValue = data[index].array[buttonIndex];
        data[index].value = selectedValue;
        delete data[index].array;
        return await _rescursiveSelect(data, index + 1);
    } else {
        return data;
    }
}