function runAppLifecycle(lifecycle, plugins, config, error, ...args) {
  try {
    const promise = Promise.all(
      plugins.filter(hasMethod(lifecycle)).map((plugin) => plugin[lifecycle](config, error, ...args)),
    );

    return raceTo(
      promise,
      MAX_LIFECYCLE_EXECUTION_TIME_IN_MS,
      `App lifecycle method ${lifecycle} took too long.`,
    );
  } catch (err) {
    return Promise.reject(err);
  }
}
Iterates through the plugins and calls the specified asynchronous lifecycle event,
returning a promise that resolves when they all are completed, or rejects if one of them
fails.

The third `config` param gets passed into the lifecycle methods as the first argument. In
this case, the app lifecycle events expect the config instance to be passed in.

This function is currently used for the lifecycle events `initialize` and `shutdown`.

@param {String} lifecycle
@param {Array<HypernovaPlugin>} plugins
@param {Config} config
@returns {Promise}
@param err {Error}