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()))
}
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<String>) -> 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<dyn FnMut(JsValue)>;
let drop_handler: Rc<RefCell<Option<C>>> = 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<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 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<A, B> {
#[wasm_bindgen]
pub struct Event {
pub local: bool,
pub from_children: bool,
origin: String,
target: ContainerID,
diff: Either<Diff, JsValue>,
@ -360,14 +387,29 @@ impl Event {
}
#[wasm_bindgen]
pub struct Transaction(Txn);
pub struct Transaction(Option<Txn>);
#[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())

View file

@ -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);
});
});

View file

@ -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();

View file

@ -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());
});