refactor: call events subs without await now

This commit is contained in:
Zixuan Chen 2023-07-30 17:08:49 +08:00
parent 88003bdffe
commit 6860132ada
4 changed files with 78 additions and 71 deletions

View file

@ -118,12 +118,19 @@ impl Loro {
Self(RefCell::new(LoroDoc::new())) Self(RefCell::new(LoroDoc::new()))
} }
pub fn txn(&self) -> Transaction { /// Create a new Loro transaction.
Transaction(self.0.borrow().txn().unwrap()) /// There can be only one transaction at a time.
} ///
/// It's caller's responsibility to call `commit` or `abort` on the transaction.
pub fn txn_with_origin(&self, origin: &str) -> Transaction { /// Transaction.free() will commit the transaction if it's not committed or aborted.
Transaction(self.0.borrow().txn_with_origin(origin).unwrap()) #[wasm_bindgen(js_name = "newTransaction")]
pub fn new_transaction(&self, origin: Option<String>) -> Transaction {
Transaction(Some(
self.0
.borrow()
.txn_with_origin(&origin.unwrap_or_default())
.unwrap(),
))
} }
#[wasm_bindgen(js_name = "clientId", method, getter)] #[wasm_bindgen(js_name = "clientId", method, getter)]
@ -247,7 +254,7 @@ impl Loro {
self.0 self.0
.borrow_mut() .borrow_mut()
.subscribe_deep(Arc::new(move |e| { .subscribe_deep(Arc::new(move |e| {
call_after_micro_task(observer.clone(), e); call_subscriber(observer.clone(), e);
})) }))
.into_u32() .into_u32()
} }
@ -268,25 +275,43 @@ impl Loro {
let origin = origin.as_string().unwrap(); let origin = origin.as_string().unwrap();
debug_log::group!("transaction with origin: {}", origin); debug_log::group!("transaction with origin: {}", origin);
let txn = self.0.borrow().txn_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); let ans = f.call1(&JsValue::NULL, &js_txn);
debug_log::group_end!(); debug_log::group_end!();
ans ans
} }
} }
fn call_after_micro_task(ob: observer::Observer, e: DiffEvent) { fn call_subscriber(ob: observer::Observer, e: DiffEvent) {
let promise = Promise::resolve(&JsValue::NULL);
type C = Closure<dyn FnMut(JsValue)>;
let drop_handler: Rc<RefCell<Option<C>>> = Rc::new(RefCell::new(None));
let copy = drop_handler.clone();
let event = Event { let event = Event {
from_children: e.from_children,
local: e.doc.local, local: e.doc.local,
origin: e.doc.origin.to_string(), origin: e.doc.origin.to_string(),
target: e.container.id.clone(), target: e.container.id.clone(),
diff: Either::A(e.container.diff.to_owned()), diff: Either::A(e.container.diff.to_owned()),
path: Either::A(e.container.path.iter().map(|x| x.1.clone()).collect()), 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<dyn FnMut(JsValue)>;
let drop_handler: Rc<RefCell<Option<C>>> = 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 closure = Closure::once(move |_: JsValue| {
let ans = ob.call1(&event.into()); let ans = ob.call1(&event.into());
drop(copy); drop(copy);
@ -294,6 +319,7 @@ fn call_after_micro_task(ob: observer::Observer, e: DiffEvent) {
console_error!("Error when calling observer: {:#?}", e); console_error!("Error when calling observer: {:#?}", e);
} }
}); });
let _ = promise.then(&closure); let _ = promise.then(&closure);
drop_handler.borrow_mut().replace(closure); drop_handler.borrow_mut().replace(closure);
} }
@ -312,6 +338,7 @@ enum Either<A, B> {
#[wasm_bindgen] #[wasm_bindgen]
pub struct Event { pub struct Event {
pub local: bool, pub local: bool,
pub from_children: bool,
origin: String, origin: String,
target: ContainerID, target: ContainerID,
diff: Either<Diff, JsValue>, diff: Either<Diff, JsValue>,
@ -360,14 +387,29 @@ impl Event {
} }
#[wasm_bindgen] #[wasm_bindgen]
pub struct Transaction(Txn); pub struct Transaction(Option<Txn>);
#[wasm_bindgen] #[wasm_bindgen]
impl Transaction { impl Transaction {
pub fn commit(self) -> JsResult<()> { pub fn commit(&mut self) -> JsResult<()> {
self.0.commit()?; if let Some(x) = self.0.take() {
x.commit()?;
}
Ok(()) 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] #[wasm_bindgen]
@ -381,7 +423,7 @@ impl LoroText {
index: usize, index: usize,
content: &str, content: &str,
) -> JsResult<()> { ) -> JsResult<()> {
self.0.insert_utf16(&mut txn.0, index, content)?; self.0.insert_utf16(txn.as_mut()?, index, content)?;
Ok(()) Ok(())
} }
@ -391,7 +433,7 @@ impl LoroText {
index: usize, index: usize,
len: usize, len: usize,
) -> JsResult<()> { ) -> JsResult<()> {
self.0.delete_utf16(&mut txn.0, index, len)?; self.0.delete_utf16(txn.as_mut()?, index, len)?;
Ok(()) Ok(())
} }
@ -417,7 +459,7 @@ impl LoroText {
let ans = loro.0.borrow_mut().subscribe( let ans = loro.0.borrow_mut().subscribe(
&self.0.id(), &self.0.id(),
Arc::new(move |e| { Arc::new(move |e| {
call_after_micro_task(observer.clone(), e); call_subscriber(observer.clone(), e);
}), }),
); );
@ -444,12 +486,12 @@ impl LoroMap {
key: &str, key: &str,
value: JsValue, value: JsValue,
) -> JsResult<()> { ) -> JsResult<()> {
self.0.insert(&mut txn.0, key, value.into())?; self.0.insert(txn.as_mut()?, key, value.into())?;
Ok(()) Ok(())
} }
pub fn __txn_delete(&mut self, txn: &mut Transaction, key: &str) -> JsResult<()> { 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(()) Ok(())
} }
@ -487,12 +529,13 @@ impl LoroMap {
"list" | "List" => ContainerType::List, "list" | "List" => ContainerType::List,
_ => return Err(JsValue::from_str(CONTAINER_TYPE_ERR)), _ => 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_ { let container = match type_ {
ContainerType::Text => LoroText(txn.0.get_text(idx)).into(), ContainerType::Text => LoroText(t.get_text(idx)).into(),
ContainerType::Map => LoroMap(txn.0.get_map(idx)).into(), ContainerType::Map => LoroMap(t.get_map(idx)).into(),
ContainerType::List => LoroList(txn.0.get_list(idx)).into(), ContainerType::List => LoroList(t.get_list(idx)).into(),
}; };
Ok(container) Ok(container)
} }
@ -502,7 +545,7 @@ impl LoroMap {
let id = loro.0.borrow_mut().subscribe( let id = loro.0.borrow_mut().subscribe(
&self.0.id(), &self.0.id(),
Arc::new(move |e| { Arc::new(move |e| {
call_after_micro_task(observer.clone(), e); call_subscriber(observer.clone(), e);
}), }),
); );
@ -526,7 +569,7 @@ impl LoroList {
index: usize, index: usize,
value: JsValue, value: JsValue,
) -> JsResult<()> { ) -> JsResult<()> {
self.0.insert(&mut txn.0, index, value.into())?; self.0.insert(txn.as_mut()?, index, value.into())?;
Ok(()) Ok(())
} }
@ -536,7 +579,7 @@ impl LoroList {
index: usize, index: usize,
len: usize, len: usize,
) -> JsResult<()> { ) -> JsResult<()> {
self.0.delete(&mut txn.0, index, len)?; self.0.delete(txn.as_mut()?, index, len)?;
Ok(()) Ok(())
} }
@ -574,11 +617,12 @@ impl LoroList {
"list" | "List" => ContainerType::List, "list" | "List" => ContainerType::List,
_ => return Err(JsValue::from_str(CONTAINER_TYPE_ERR)), _ => 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 { let container = match _type {
ContainerType::Text => LoroText(txn.0.get_text(idx)).into(), ContainerType::Text => LoroText(t.get_text(idx)).into(),
ContainerType::Map => LoroMap(txn.0.get_map(idx)).into(), ContainerType::Map => LoroMap(t.get_map(idx)).into(),
ContainerType::List => LoroList(txn.0.get_list(idx)).into(), ContainerType::List => LoroList(t.get_list(idx)).into(),
}; };
Ok(container) Ok(container)
} }
@ -588,7 +632,7 @@ impl LoroList {
let ans = loro.0.borrow_mut().subscribe( let ans = loro.0.borrow_mut().subscribe(
&self.0.id(), &self.0.id(),
Arc::new(move |e| { Arc::new(move |e| {
call_after_micro_task(observer.clone(), e); call_subscriber(observer.clone(), e);
}), }),
); );
Ok(ans.into_u32()) Ok(ans.into_u32())

View file

@ -22,7 +22,6 @@ describe("event", () => {
loro.transact((tx) => { loro.transact((tx) => {
text.insert(tx, 0, "123"); text.insert(tx, 0, "123");
}); });
await zeroMs();
expect(lastEvent?.target).toEqual(id); expect(lastEvent?.target).toEqual(id);
}); });
@ -39,7 +38,6 @@ describe("event", () => {
return subMap; return subMap;
}); });
await zeroMs();
expect(lastEvent?.path).toStrictEqual(["map", "sub"]); expect(lastEvent?.path).toStrictEqual(["map", "sub"]);
const text = loro.transact((tx) => { const text = loro.transact((tx) => {
const list = subMap.insertContainer(tx, "list", "List"); const list = subMap.insertContainer(tx, "list", "List");
@ -47,11 +45,9 @@ describe("event", () => {
const text = list.insertContainer(tx, 1, "Text"); const text = list.insertContainer(tx, 1, "Text");
return text; return text;
}); });
await zeroMs();
loro.transact((tx) => { loro.transact((tx) => {
text.insert(tx, 0, "3"); text.insert(tx, 0, "3");
}); });
await zeroMs();
expect(lastEvent?.path).toStrictEqual(["map", "sub", "list", 1]); expect(lastEvent?.path).toStrictEqual(["map", "sub", "list", 1]);
}); });
@ -65,14 +61,12 @@ describe("event", () => {
loro.transact(tx => { loro.transact(tx => {
text.insert(tx, 0, "3"); text.insert(tx, 0, "3");
}) })
await zeroMs();
expect(lastEvent?.diff).toStrictEqual( expect(lastEvent?.diff).toStrictEqual(
{ type: "text", diff: [{ type: "insert", value: "3" }] } as TextDiff, { type: "text", diff: [{ type: "insert", value: "3" }] } as TextDiff,
); );
loro.transact(tx => { loro.transact(tx => {
text.insert(tx, 1, "12"); text.insert(tx, 1, "12");
}) })
await zeroMs();
expect(lastEvent?.diff).toStrictEqual( expect(lastEvent?.diff).toStrictEqual(
{ {
type: "text", type: "text",
@ -91,14 +85,12 @@ describe("event", () => {
loro.transact(tx => { loro.transact(tx => {
text.insert(tx, 0, "3"); text.insert(tx, 0, "3");
}) })
await zeroMs();
expect(lastEvent?.diff).toStrictEqual( expect(lastEvent?.diff).toStrictEqual(
{ type: "list", diff: [{ type: "insert", value: ["3"] }] } as ListDiff, { type: "list", diff: [{ type: "insert", value: ["3"] }] } as ListDiff,
); );
loro.transact(tx => { loro.transact(tx => {
text.insert(tx, 1, "12"); text.insert(tx, 1, "12");
}) })
await zeroMs();
expect(lastEvent?.diff).toStrictEqual( expect(lastEvent?.diff).toStrictEqual(
{ {
type: "list", type: "list",
@ -118,7 +110,6 @@ describe("event", () => {
map.set(tx, "0", "3"); map.set(tx, "0", "3");
map.set(tx, "1", "2"); map.set(tx, "1", "2");
}); });
await zeroMs();
expect(lastEvent?.diff).toStrictEqual( expect(lastEvent?.diff).toStrictEqual(
{ {
type: "map", type: "map",
@ -132,7 +123,6 @@ describe("event", () => {
map.set(tx, "0", "0"); map.set(tx, "0", "0");
map.set(tx, "1", "1"); map.set(tx, "1", "1");
}); });
await zeroMs();
expect(lastEvent?.diff).toStrictEqual( expect(lastEvent?.diff).toStrictEqual(
{ {
type: "map", type: "map",
@ -165,7 +155,6 @@ describe("event", () => {
loro.transact(tx => { loro.transact(tx => {
text.insert(tx, 1, "456"); text.insert(tx, 1, "456");
}); });
await zeroMs();
expect(ran).toBeTruthy(); expect(ran).toBeTruthy();
// subscribeOnce test // subscribeOnce test
expect(text.toString()).toEqual("145623"); expect(text.toString()).toEqual("145623");
@ -192,19 +181,15 @@ describe("event", () => {
loro.transact(tx => loro.transact(tx =>
map.insertContainer(tx, "sub", "Map") map.insertContainer(tx, "sub", "Map")
); );
await zeroMs();
expect(times).toBe(1); expect(times).toBe(1);
const text = loro.transact(tx => subMap.insertContainer(tx, "k", "Text")); const text = loro.transact(tx => subMap.insertContainer(tx, "k", "Text"));
await zeroMs();
expect(times).toBe(2); expect(times).toBe(2);
loro.transact(tx => text.insert(tx, 0, "123")); loro.transact(tx => text.insert(tx, 0, "123"));
await zeroMs();
expect(times).toBe(3); expect(times).toBe(3);
// unsubscribe // unsubscribe
loro.unsubscribe(sub); loro.unsubscribe(sub);
loro.transact(tx => text.insert(tx, 0, "123")); loro.transact(tx => text.insert(tx, 0, "123"));
await zeroMs();
expect(times).toBe(3); expect(times).toBe(3);
}); });
@ -217,16 +202,13 @@ describe("event", () => {
}); });
const text = loro.transact(tx => list.insertContainer(tx, 0, "Text")); const text = loro.transact(tx => list.insertContainer(tx, 0, "Text"));
await zeroMs();
expect(times).toBe(1); expect(times).toBe(1);
loro.transact(tx => text.insert(tx, 0, "123")); loro.transact(tx => text.insert(tx, 0, "123"));
await zeroMs();
expect(times).toBe(2); expect(times).toBe(2);
// unsubscribe // unsubscribe
loro.unsubscribe(sub); loro.unsubscribe(sub);
loro.transact(tx => text.insert(tx, 0, "123")); loro.transact(tx => text.insert(tx, 0, "123"));
await zeroMs();
expect(times).toBe(2); expect(times).toBe(2);
}); });
}); });
@ -257,19 +239,15 @@ describe("event", () => {
} }
}); });
loro.transact(tx => text.insert(tx, 0, "你好")); loro.transact(tx => text.insert(tx, 0, "你好"));
await zeroMs();
expect(text.toString()).toBe(string); expect(text.toString()).toBe(string);
loro.transact(tx => text.insert(tx, 1, "世界")); loro.transact(tx => text.insert(tx, 1, "世界"));
await zeroMs();
expect(text.toString()).toBe(string); expect(text.toString()).toBe(string);
loro.transact(tx => text.insert(tx, 2, "👍")); loro.transact(tx => text.insert(tx, 2, "👍"));
await zeroMs();
expect(text.toString()).toBe(string); expect(text.toString()).toBe(string);
loro.transact(tx => text.insert(tx, 2, "♪(^∇^*)")); loro.transact(tx => text.insert(tx, 2, "♪(^∇^*)"));
await zeroMs();
expect(text.toString()).toBe(string); expect(text.toString()).toBe(string);
}); });
}); });

View file

@ -14,7 +14,7 @@ describe("Frontiers", () => {
it("two clients", () => { it("two clients", () => {
const doc = new Loro(); const doc = new Loro();
const text = doc.getText("text"); const text = doc.getText("text");
const txn = doc.txn(); const txn = doc.newTransaction("");
text.insert(txn, 0, "0"); text.insert(txn, 0, "0");
txn.commit(); txn.commit();

View file

@ -1,16 +1,11 @@
import { assertType, describe, expect, it } from "vitest"; import { assertType, describe, expect, it } from "vitest";
import { import {
Delta,
ListDiff,
Loro, Loro,
LoroEvent,
LoroList, LoroList,
LoroMap, LoroMap,
MapDiff as MapDiff,
PrelimList, PrelimList,
PrelimMap, PrelimMap,
PrelimText, PrelimText,
TextDiff,
Transaction, Transaction,
} from "../src"; } from "../src";
import { expectTypeOf } from "vitest"; import { expectTypeOf } from "vitest";
@ -35,7 +30,6 @@ describe("transaction", () => {
text.insert(txn, 0, "hello world"); text.insert(txn, 0, "hello world");
assertEquals(count, 0); assertEquals(count, 0);
}); });
await one_ms();
assertEquals(count, 1); assertEquals(count, 1);
}); });
@ -55,7 +49,6 @@ describe("transaction", () => {
text.insert(txn, 0, "hello world"); text.insert(txn, 0, "hello world");
assertEquals(count, 0); assertEquals(count, 0);
}, "origin"); }, "origin");
await one_ms();
assertEquals(count, 1); assertEquals(count, 1);
}); });
}); });
@ -81,18 +74,15 @@ describe("subscribe", () => {
text.insert(txn, 0, "hello world"); text.insert(txn, 0, "hello world");
}) })
await one_ms();
assertEquals(count, 2); assertEquals(count, 2);
loro.transact((txn) => { loro.transact((txn) => {
text.insert(txn, 0, "hello world"); text.insert(txn, 0, "hello world");
}); });
await one_ms();
assertEquals(count, 3); assertEquals(count, 3);
loro.unsubscribe(sub); loro.unsubscribe(sub);
loro.transact(txn => { loro.transact(txn => {
text.insert(txn, 0, "hello world"); text.insert(txn, 0, "hello world");
}) })
await one_ms();
assertEquals(count, 3); assertEquals(count, 3);
}); });
@ -109,7 +99,6 @@ describe("subscribe", () => {
text.insert(txn, 0, "hello world"); text.insert(txn, 0, "hello world");
}) })
await one_ms();
assertEquals(count, 1); assertEquals(count, 1);
loro.transact(txn => { loro.transact(txn => {
text.insert(txn, 0, "hello world"); text.insert(txn, 0, "hello world");
@ -128,18 +117,15 @@ describe("subscribe", () => {
loro.transact(loro => { loro.transact(loro => {
text.insert(loro, 0, "hello world"); text.insert(loro, 0, "hello world");
}) })
await one_ms();
assertEquals(count, 1); assertEquals(count, 1);
loro.transact(loro => { loro.transact(loro => {
text.insert(loro, 0, "hello world"); text.insert(loro, 0, "hello world");
}) })
await one_ms();
assertEquals(count, 2); assertEquals(count, 2);
loro.unsubscribe(sub); loro.unsubscribe(sub);
loro.transact(loro => { loro.transact(loro => {
text.insert(loro, 0, "hello world"); text.insert(loro, 0, "hello world");
}) })
await one_ms();
assertEquals(count, 2); assertEquals(count, 2);
}); });
}); });
@ -170,7 +156,6 @@ describe("sync", () => {
aText.insert(txn, 0, "abc"); aText.insert(txn, 0, "abc");
}); });
await one_ms();
assertEquals(aText.toString(), bText.toString()); assertEquals(aText.toString(), bText.toString());
}); });