[js_runtime] Modernize and simplify `_AsyncRun._scheduleImmediate`.

Use browser's `queueMicrotask` if available.

Remove `MutationObserver` method, since `queueMicrotask` is available on all supported browsers.

Simplify by removing `internalCallback`. This is no longer needed (it used to do more than just call the callback).

Issue: #20055
Change-Id: Ic866cdd77787fc45456f65a6618fe66afcbcb970
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/444392
Reviewed-by: Mayank Patke <fishythefish@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
diff --git a/sdk/lib/_internal/js_runtime/lib/async_patch.dart b/sdk/lib/_internal/js_runtime/lib/async_patch.dart
index 0d53e6b..1ac1cf8 100644
--- a/sdk/lib/_internal/js_runtime/lib/async_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/async_patch.dart
@@ -26,6 +26,8 @@
   }
 }
 
+typedef _ScheduleImmediateFn = void Function(void Function());
+
 @patch
 class _AsyncRun {
   @patch
@@ -34,78 +36,33 @@
   }
 
   // Lazily initialized.
-  static final Function _scheduleImmediateClosure =
+  static final _ScheduleImmediateFn _scheduleImmediateClosure =
       _initializeScheduleImmediate();
 
-  static Function _initializeScheduleImmediate() {
+  static _ScheduleImmediateFn _initializeScheduleImmediate() {
     requiresPreamble();
     if (JS('', 'self.scheduleImmediate') != null) {
       return _scheduleImmediateJsOverride;
     }
-    if (JS('', 'self.MutationObserver') != null &&
-        JS('', 'self.document') != null) {
-      // Use mutationObservers.
-      var div = JS('', 'self.document.createElement("div")');
-      var span = JS('', 'self.document.createElement("span")');
-      void Function()? storedCallback;
 
-      internalCallback(_) {
-        var f = storedCallback;
-        storedCallback = null;
-        f!();
-      }
-
-      var observer = JS(
-        '',
-        'new self.MutationObserver(#)',
-        convertDartClosureToJS(internalCallback, 1),
-      );
-      JS('', '#.observe(#, { childList: true })', observer, div);
-
-      return (void callback()) {
-        assert(storedCallback == null);
-        storedCallback = callback;
-        // Because of a broken shadow-dom polyfill we have to change the
-        // children instead a cheap property.
-        JS(
-          '',
-          '#.firstChild ? #.removeChild(#): #.appendChild(#)',
-          div,
-          div,
-          span,
-          div,
-          span,
-        );
-      };
-    } else if (JS('', 'self.setImmediate') != null) {
-      return _scheduleImmediateWithSetImmediate;
+    if (JS('', 'typeof self.queueMicrotask == "function"')) {
+      return _scheduleImmediateWithQueueMicrotask;
     }
-    // TODO(20055): We should use DOM promises when available.
+
+    // The fallback is not quite right as it uses the macro task queue.
     return _scheduleImmediateWithTimer;
   }
 
   static void _scheduleImmediateJsOverride(void callback()) {
-    internalCallback() {
-      callback();
-    }
-
-    JS(
-      'void',
-      'self.scheduleImmediate(#)',
-      convertDartClosureToJS(internalCallback, 0),
-    );
+    JS('', 'self.scheduleImmediate(#)', convertDartClosureToJS(callback, 0));
   }
 
-  static void _scheduleImmediateWithSetImmediate(void callback()) {
-    internalCallback() {
-      callback();
-    }
-
-    JS(
-      'void',
-      'self.setImmediate(#)',
-      convertDartClosureToJS(internalCallback, 0),
-    );
+  /// `queueMicrotask` is available in all supported browsers in
+  /// [windows](https://developer.mozilla.org/en-US/docs/Web/API/Window/queueMicrotask#browser_compatibility)
+  /// and
+  /// [workers](https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/queueMicrotask#browser_compatibility).
+  static void _scheduleImmediateWithQueueMicrotask(void callback()) {
+    JS('', 'self.queueMicrotask(#)', convertDartClosureToJS(callback, 0));
   }
 
   static void _scheduleImmediateWithTimer(void callback()) {