From 6860132ada977e9c4f1ea871a21ce67a856f23df Mon Sep 17 00:00:00 2001 From: Zixuan Chen Date: Sun, 30 Jul 2023 17:08:49 +0800 Subject: [PATCH] refactor: call events subs without await now --- crates/loro-wasm/src/lib.rs | 110 ++++++++++++++++++++++---------- loro-js/tests/event.test.ts | 22 ------- loro-js/tests/frontiers.test.ts | 2 +- loro-js/tests/misc.test.ts | 15 ----- 4 files changed, 78 insertions(+), 71 deletions(-) diff --git a/crates/loro-wasm/src/lib.rs b/crates/loro-wasm/src/lib.rs index 6d082646..b918f379 100644 --- a/crates/loro-wasm/src/lib.rs +++ b/crates/loro-wasm/src/lib.rs @@ -118,12 +118,19 @@ impl Loro { Self(RefCell::new(LoroDoc::new())) } - pub fn txn(&self) -> Transaction { - Transaction(self.0.borrow().txn().unwrap()) - } - - pub fn txn_with_origin(&self, origin: &str) -> Transaction { - Transaction(self.0.borrow().txn_with_origin(origin).unwrap()) + /// Create a new Loro transaction. + /// There can be only one transaction at a time. + /// + /// It's caller's responsibility to call `commit` or `abort` on the transaction. + /// Transaction.free() will commit the transaction if it's not committed or aborted. + #[wasm_bindgen(js_name = "newTransaction")] + pub fn new_transaction(&self, origin: Option) -> Transaction { + Transaction(Some( + self.0 + .borrow() + .txn_with_origin(&origin.unwrap_or_default()) + .unwrap(), + )) } #[wasm_bindgen(js_name = "clientId", method, getter)] @@ -247,7 +254,7 @@ impl Loro { self.0 .borrow_mut() .subscribe_deep(Arc::new(move |e| { - call_after_micro_task(observer.clone(), e); + call_subscriber(observer.clone(), e); })) .into_u32() } @@ -268,25 +275,43 @@ impl Loro { let origin = origin.as_string().unwrap(); debug_log::group!("transaction with origin: {}", origin); let txn = self.0.borrow().txn_with_origin(&origin)?; - let js_txn = JsValue::from(Transaction(txn)); + let js_txn = JsValue::from(Transaction(Some(txn))); let ans = f.call1(&JsValue::NULL, &js_txn); debug_log::group_end!(); ans } } -fn call_after_micro_task(ob: observer::Observer, e: DiffEvent) { - let promise = Promise::resolve(&JsValue::NULL); - type C = Closure; - let drop_handler: Rc>> = Rc::new(RefCell::new(None)); - let copy = drop_handler.clone(); +fn call_subscriber(ob: observer::Observer, e: DiffEvent) { let event = Event { + from_children: e.from_children, local: e.doc.local, origin: e.doc.origin.to_string(), target: e.container.id.clone(), diff: Either::A(e.container.diff.to_owned()), path: Either::A(e.container.path.iter().map(|x| x.1.clone()).collect()), }; + + if let Err(e) = ob.call1(&event.into()) { + console_error!("Error when calling observer: {:#?}", e); + } +} + +#[allow(unused)] +fn call_after_micro_task(ob: observer::Observer, e: DiffEvent) { + let promise = Promise::resolve(&JsValue::NULL); + type C = Closure; + let drop_handler: Rc>> = Rc::new(RefCell::new(None)); + let copy = drop_handler.clone(); + let event = Event { + from_children: e.from_children, + local: e.doc.local, + origin: e.doc.origin.to_string(), + target: e.container.id.clone(), + diff: Either::A(e.container.diff.to_owned()), + path: Either::A(e.container.path.iter().map(|x| x.1.clone()).collect()), + }; + let closure = Closure::once(move |_: JsValue| { let ans = ob.call1(&event.into()); drop(copy); @@ -294,6 +319,7 @@ fn call_after_micro_task(ob: observer::Observer, e: DiffEvent) { console_error!("Error when calling observer: {:#?}", e); } }); + let _ = promise.then(&closure); drop_handler.borrow_mut().replace(closure); } @@ -312,6 +338,7 @@ enum Either { #[wasm_bindgen] pub struct Event { pub local: bool, + pub from_children: bool, origin: String, target: ContainerID, diff: Either, @@ -360,14 +387,29 @@ impl Event { } #[wasm_bindgen] -pub struct Transaction(Txn); +pub struct Transaction(Option); #[wasm_bindgen] impl Transaction { - pub fn commit(self) -> JsResult<()> { - self.0.commit()?; + pub fn commit(&mut self) -> JsResult<()> { + if let Some(x) = self.0.take() { + x.commit()?; + } Ok(()) } + + pub fn abort(&mut self) -> JsResult<()> { + if let Some(x) = self.0.take() { + x.abort(); + } + Ok(()) + } + + fn as_mut(&mut self) -> JsResult<&mut Txn> { + self.0 + .as_mut() + .ok_or_else(|| JsValue::from_str("Transaction is aborted")) + } } #[wasm_bindgen] @@ -381,7 +423,7 @@ impl LoroText { index: usize, content: &str, ) -> JsResult<()> { - self.0.insert_utf16(&mut txn.0, index, content)?; + self.0.insert_utf16(txn.as_mut()?, index, content)?; Ok(()) } @@ -391,7 +433,7 @@ impl LoroText { index: usize, len: usize, ) -> JsResult<()> { - self.0.delete_utf16(&mut txn.0, index, len)?; + self.0.delete_utf16(txn.as_mut()?, index, len)?; Ok(()) } @@ -417,7 +459,7 @@ impl LoroText { let ans = loro.0.borrow_mut().subscribe( &self.0.id(), Arc::new(move |e| { - call_after_micro_task(observer.clone(), e); + call_subscriber(observer.clone(), e); }), ); @@ -444,12 +486,12 @@ impl LoroMap { key: &str, value: JsValue, ) -> JsResult<()> { - self.0.insert(&mut txn.0, key, value.into())?; + self.0.insert(txn.as_mut()?, key, value.into())?; Ok(()) } pub fn __txn_delete(&mut self, txn: &mut Transaction, key: &str) -> JsResult<()> { - self.0.delete(&mut txn.0, key)?; + self.0.delete(txn.as_mut()?, key)?; Ok(()) } @@ -487,12 +529,13 @@ impl LoroMap { "list" | "List" => ContainerType::List, _ => return Err(JsValue::from_str(CONTAINER_TYPE_ERR)), }; - let idx = self.0.insert_container(&mut txn.0, key, type_)?; + let t = txn.as_mut()?; + let idx = self.0.insert_container(t, key, type_)?; let container = match type_ { - ContainerType::Text => LoroText(txn.0.get_text(idx)).into(), - ContainerType::Map => LoroMap(txn.0.get_map(idx)).into(), - ContainerType::List => LoroList(txn.0.get_list(idx)).into(), + ContainerType::Text => LoroText(t.get_text(idx)).into(), + ContainerType::Map => LoroMap(t.get_map(idx)).into(), + ContainerType::List => LoroList(t.get_list(idx)).into(), }; Ok(container) } @@ -502,7 +545,7 @@ impl LoroMap { let id = loro.0.borrow_mut().subscribe( &self.0.id(), Arc::new(move |e| { - call_after_micro_task(observer.clone(), e); + call_subscriber(observer.clone(), e); }), ); @@ -526,7 +569,7 @@ impl LoroList { index: usize, value: JsValue, ) -> JsResult<()> { - self.0.insert(&mut txn.0, index, value.into())?; + self.0.insert(txn.as_mut()?, index, value.into())?; Ok(()) } @@ -536,7 +579,7 @@ impl LoroList { index: usize, len: usize, ) -> JsResult<()> { - self.0.delete(&mut txn.0, index, len)?; + self.0.delete(txn.as_mut()?, index, len)?; Ok(()) } @@ -574,11 +617,12 @@ impl LoroList { "list" | "List" => ContainerType::List, _ => return Err(JsValue::from_str(CONTAINER_TYPE_ERR)), }; - let idx = self.0.insert_container(&mut txn.0, pos, _type)?; + let t = txn.as_mut()?; + let idx = self.0.insert_container(t, pos, _type)?; let container = match _type { - ContainerType::Text => LoroText(txn.0.get_text(idx)).into(), - ContainerType::Map => LoroMap(txn.0.get_map(idx)).into(), - ContainerType::List => LoroList(txn.0.get_list(idx)).into(), + ContainerType::Text => LoroText(t.get_text(idx)).into(), + ContainerType::Map => LoroMap(t.get_map(idx)).into(), + ContainerType::List => LoroList(t.get_list(idx)).into(), }; Ok(container) } @@ -588,7 +632,7 @@ impl LoroList { let ans = loro.0.borrow_mut().subscribe( &self.0.id(), Arc::new(move |e| { - call_after_micro_task(observer.clone(), e); + call_subscriber(observer.clone(), e); }), ); Ok(ans.into_u32()) diff --git a/loro-js/tests/event.test.ts b/loro-js/tests/event.test.ts index 41c183a3..ba6b4056 100644 --- a/loro-js/tests/event.test.ts +++ b/loro-js/tests/event.test.ts @@ -22,7 +22,6 @@ describe("event", () => { loro.transact((tx) => { text.insert(tx, 0, "123"); }); - await zeroMs(); expect(lastEvent?.target).toEqual(id); }); @@ -39,7 +38,6 @@ describe("event", () => { return subMap; }); - await zeroMs(); expect(lastEvent?.path).toStrictEqual(["map", "sub"]); const text = loro.transact((tx) => { const list = subMap.insertContainer(tx, "list", "List"); @@ -47,11 +45,9 @@ describe("event", () => { const text = list.insertContainer(tx, 1, "Text"); return text; }); - await zeroMs(); loro.transact((tx) => { text.insert(tx, 0, "3"); }); - await zeroMs(); expect(lastEvent?.path).toStrictEqual(["map", "sub", "list", 1]); }); @@ -65,14 +61,12 @@ describe("event", () => { loro.transact(tx => { text.insert(tx, 0, "3"); }) - await zeroMs(); expect(lastEvent?.diff).toStrictEqual( { type: "text", diff: [{ type: "insert", value: "3" }] } as TextDiff, ); loro.transact(tx => { text.insert(tx, 1, "12"); }) - await zeroMs(); expect(lastEvent?.diff).toStrictEqual( { type: "text", @@ -91,14 +85,12 @@ describe("event", () => { loro.transact(tx => { text.insert(tx, 0, "3"); }) - await zeroMs(); expect(lastEvent?.diff).toStrictEqual( { type: "list", diff: [{ type: "insert", value: ["3"] }] } as ListDiff, ); loro.transact(tx => { text.insert(tx, 1, "12"); }) - await zeroMs(); expect(lastEvent?.diff).toStrictEqual( { type: "list", @@ -118,7 +110,6 @@ describe("event", () => { map.set(tx, "0", "3"); map.set(tx, "1", "2"); }); - await zeroMs(); expect(lastEvent?.diff).toStrictEqual( { type: "map", @@ -132,7 +123,6 @@ describe("event", () => { map.set(tx, "0", "0"); map.set(tx, "1", "1"); }); - await zeroMs(); expect(lastEvent?.diff).toStrictEqual( { type: "map", @@ -165,7 +155,6 @@ describe("event", () => { loro.transact(tx => { text.insert(tx, 1, "456"); }); - await zeroMs(); expect(ran).toBeTruthy(); // subscribeOnce test expect(text.toString()).toEqual("145623"); @@ -192,19 +181,15 @@ describe("event", () => { loro.transact(tx => map.insertContainer(tx, "sub", "Map") ); - await zeroMs(); expect(times).toBe(1); const text = loro.transact(tx => subMap.insertContainer(tx, "k", "Text")); - await zeroMs(); expect(times).toBe(2); loro.transact(tx => text.insert(tx, 0, "123")); - await zeroMs(); expect(times).toBe(3); // unsubscribe loro.unsubscribe(sub); loro.transact(tx => text.insert(tx, 0, "123")); - await zeroMs(); expect(times).toBe(3); }); @@ -217,16 +202,13 @@ describe("event", () => { }); const text = loro.transact(tx => list.insertContainer(tx, 0, "Text")); - await zeroMs(); expect(times).toBe(1); loro.transact(tx => text.insert(tx, 0, "123")); - await zeroMs(); expect(times).toBe(2); // unsubscribe loro.unsubscribe(sub); loro.transact(tx => text.insert(tx, 0, "123")); - await zeroMs(); expect(times).toBe(2); }); }); @@ -257,19 +239,15 @@ describe("event", () => { } }); loro.transact(tx => text.insert(tx, 0, "δ½ ε₯½")); - await zeroMs(); expect(text.toString()).toBe(string); loro.transact(tx => text.insert(tx, 1, "δΈ–η•Œ")); - await zeroMs(); expect(text.toString()).toBe(string); loro.transact(tx => text.insert(tx, 2, "πŸ‘")); - await zeroMs(); expect(text.toString()).toBe(string); loro.transact(tx => text.insert(tx, 2, "β™ͺ(^βˆ‡^*)")); - await zeroMs(); expect(text.toString()).toBe(string); }); }); diff --git a/loro-js/tests/frontiers.test.ts b/loro-js/tests/frontiers.test.ts index 479f3df0..c42b6c83 100644 --- a/loro-js/tests/frontiers.test.ts +++ b/loro-js/tests/frontiers.test.ts @@ -14,7 +14,7 @@ describe("Frontiers", () => { it("two clients", () => { const doc = new Loro(); const text = doc.getText("text"); - const txn = doc.txn(); + const txn = doc.newTransaction(""); text.insert(txn, 0, "0"); txn.commit(); diff --git a/loro-js/tests/misc.test.ts b/loro-js/tests/misc.test.ts index c38741a8..3484dfe4 100644 --- a/loro-js/tests/misc.test.ts +++ b/loro-js/tests/misc.test.ts @@ -1,16 +1,11 @@ import { assertType, describe, expect, it } from "vitest"; import { - Delta, - ListDiff, Loro, - LoroEvent, LoroList, LoroMap, - MapDiff as MapDiff, PrelimList, PrelimMap, PrelimText, - TextDiff, Transaction, } from "../src"; import { expectTypeOf } from "vitest"; @@ -35,7 +30,6 @@ describe("transaction", () => { text.insert(txn, 0, "hello world"); assertEquals(count, 0); }); - await one_ms(); assertEquals(count, 1); }); @@ -55,7 +49,6 @@ describe("transaction", () => { text.insert(txn, 0, "hello world"); assertEquals(count, 0); }, "origin"); - await one_ms(); assertEquals(count, 1); }); }); @@ -81,18 +74,15 @@ describe("subscribe", () => { text.insert(txn, 0, "hello world"); }) - await one_ms(); assertEquals(count, 2); loro.transact((txn) => { text.insert(txn, 0, "hello world"); }); - await one_ms(); assertEquals(count, 3); loro.unsubscribe(sub); loro.transact(txn => { text.insert(txn, 0, "hello world"); }) - await one_ms(); assertEquals(count, 3); }); @@ -109,7 +99,6 @@ describe("subscribe", () => { text.insert(txn, 0, "hello world"); }) - await one_ms(); assertEquals(count, 1); loro.transact(txn => { text.insert(txn, 0, "hello world"); @@ -128,18 +117,15 @@ describe("subscribe", () => { loro.transact(loro => { text.insert(loro, 0, "hello world"); }) - await one_ms(); assertEquals(count, 1); loro.transact(loro => { text.insert(loro, 0, "hello world"); }) - await one_ms(); assertEquals(count, 2); loro.unsubscribe(sub); loro.transact(loro => { text.insert(loro, 0, "hello world"); }) - await one_ms(); assertEquals(count, 2); }); }); @@ -170,7 +156,6 @@ describe("sync", () => { aText.insert(txn, 0, "abc"); }); - await one_ms(); assertEquals(aText.toString(), bText.toString()); });