decodeAudioData method Null safety

Future<AudioBuffer> decodeAudioData(
  1. ByteBuffer audioData,
  2. [DecodeSuccessCallback? successCallback,
  3. DecodeErrorCallback? errorCallback]


Future<AudioBuffer> decodeAudioData(ByteBuffer audioData,
    [DecodeSuccessCallback? successCallback,
    DecodeErrorCallback? errorCallback]) {
  // Both callbacks need to be provided if they're being used.
  assert((successCallback == null) == (errorCallback == null));
  // `decodeAudioData` can exist either in the older callback syntax or the
  // newer `Promise`-based syntax that also accepts callbacks. In the former,
  // we synthesize a `Future` to be consistent.
  // For more details:
  final completer = Completer<Object>();
  var errorInCallbackIsNull = false;

  void success(AudioBuffer decodedData) {

  final nullErrorString =
      '[AudioContext.decodeAudioData] completed with a null error.';

  void error(DomException? error) {
    // Safari has a bug where it may return null for the error callback. In
    // the case where the Safari version still returns a `Promise` and the
    // error is not null after the `Promise` is finished, the error callback
    // is called instead in the `Promise`'s `catch` block. Otherwise, and in
    // the case where a `Promise` is not returned by the API at all, the
    // callback never gets called (for backwards compatibility, it can not
    // accept null). Instead, the `Future` completes with a custom string,
    // indicating that null was given.
    if (error != null) {
      // Note that we `complete` and not `completeError`. This is to make sure
      // that errors in the `Completer` are not thrown if the call gets back
      // a `Promise`.
    } else {
      errorInCallbackIsNull = true;

  var decodeResult;
  if (successCallback == null) {
    decodeResult =
        JS("creates:AudioBuffer;", "#.decodeAudioData(#)", this, audioData);
  } else {
    decodeResult = JS(
        "#.decodeAudioData(#, #, #)",
        convertDartClosureToJS(success, 1),
        convertDartClosureToJS(error, 1));

  if (decodeResult != null) {
    // Promise-based syntax.
    return promiseToFuture<AudioBuffer>(decodeResult).catchError((error) {
      // If the error was null in the callback, but no longer is now that the
      // `Promise` is finished, call the error callback. If it's still null,
      // throw the error string. This is to handle the aforementioned bug in
      // Safari.
      if (errorInCallbackIsNull) {
        if (error != null) {
        } else {
          throw nullErrorString;
      throw error;

  // Callback-based syntax. We use the above completer to synthesize a
  // `Future` from the callback values. Since we don't use `completeError`
  // above, `then` is used to simulate an error.
  return completer.future.then((value) {
    if (value is AudioBuffer) return value;
    throw value;