Bläddra i källkod

feat(ipc): preserve channel message order (#9070)

Lucas Fernandes Nogueira 1 år sedan
förälder
incheckning
e62ca4ee95

+ 6 - 0
.changes/preserve-channel-order.md

@@ -0,0 +1,6 @@
+---
+"tauri": patch:enhance
+"@tauri-apps/api": patch:enhance
+---
+
+Added a mechanism to preserve channel message order.

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
core/tauri/scripts/bundle.global.js


+ 11 - 3
core/tauri/src/ipc/channel.rs

@@ -6,7 +6,7 @@ use std::{
   collections::HashMap,
   str::FromStr,
   sync::{
-    atomic::{AtomicU32, Ordering},
+    atomic::{AtomicU32, AtomicUsize, Ordering},
     Arc, Mutex,
   },
 };
@@ -132,18 +132,26 @@ impl Channel {
   }
 
   pub(crate) fn from_callback_fn<R: Runtime>(webview: Webview<R>, callback: CallbackFn) -> Self {
+    let counter = AtomicUsize::new(0);
+
     Channel::new_with_id(callback.0, move |body| {
       let data_id = CHANNEL_DATA_COUNTER.fetch_add(1, Ordering::Relaxed);
+
       webview
         .state::<ChannelDataIpcQueue>()
         .0
         .lock()
         .unwrap()
         .insert(data_id, body);
+
+      let i = counter.fetch_add(1, Ordering::Relaxed);
+
       webview.eval(&format!(
-        "window.__TAURI_INTERNALS__.invoke('{FETCH_CHANNEL_DATA_COMMAND}', null, {{ headers: {{ '{CHANNEL_ID_HEADER_NAME}': '{data_id}' }} }}).then(window['_' + {}]).catch(console.error)",
+        "window.__TAURI_INTERNALS__.invoke('{FETCH_CHANNEL_DATA_COMMAND}', null, {{ headers: {{ '{CHANNEL_ID_HEADER_NAME}': '{data_id}' }} }}).then((response) => window['_' + {}]({{ message: response, id: {i} }})).catch(console.error)",
         callback.0
-      ))
+      ))?;
+
+      Ok(())
     })
   }
 

+ 36 - 3
tooling/api/src/core.ts

@@ -31,11 +31,44 @@ class Channel<T = unknown> {
   #onmessage: (response: T) => void = () => {
     // no-op
   }
+  #nextMessageId = 0
+  #pendingMessages: Record<string, T> = {}
 
   constructor() {
-    this.id = transformCallback((response: T) => {
-      this.#onmessage(response)
-    })
+    this.id = transformCallback(
+      ({ message, id }: { message: T; id: number }) => {
+        // the id is used as a mechanism to preserve message order
+        if (id === this.#nextMessageId) {
+          this.#nextMessageId = id + 1
+          this.#onmessage(message)
+
+          // process pending messages
+          const pendingMessageIds = Object.keys(this.#pendingMessages)
+          if (pendingMessageIds.length > 0) {
+            let nextId = id + 1
+            for (const pendingId of pendingMessageIds.sort()) {
+              // if we have the next message, process it
+              if (parseInt(pendingId) === nextId) {
+                // eslint-disable-next-line security/detect-object-injection
+                const message = this.#pendingMessages[pendingId]
+                // eslint-disable-next-line security/detect-object-injection
+                delete this.#pendingMessages[pendingId]
+
+                this.#onmessage(message)
+
+                // move the id counter to the next message to check
+                nextId += 1
+              } else {
+                // we do not have the next message, let's wait
+                break
+              }
+            }
+          }
+        } else {
+          this.#pendingMessages[id.toString()] = message
+        }
+      }
+    )
   }
 
   set onmessage(handler: (response: T) => void) {

Vissa filer visades inte eftersom för många filer har ändrats