Temel kavramlar
doWork ile Asenkron Sonuç
Bir sözü bileşenin ömrüne bağlayan `doWork`: dönen söz, MJX108, `catch` ve `finally`, `using` ile farkı.
doWork(söz) verilen sözü bileşenin ömrüne bağlar ve yeni bir söz döndürür. Bileşen yaşarken bu söz verilenin kendisi gibi davranır: değer then'e, ret catch'e gider. Bileşen serbest bırakıldıktan sonra söz reddedilmez; değerin ya da reddin yerine MJX108 kodlu bir MotifError gelir. using'den farkı bir söz döndürmesidir: sonucu zincirleyebilir, catch ve finally ekleyebilir, çağırana geri verebilirsiniz.
Dönen söz
| Verilen söz | Bileşen yaşıyorsa | Bileşen serbest bırakılmışsa |
|---|---|---|
| çözülür | then değeri alır. |
then bir MotifError (MJX108) alır. |
| reddedilir | catch nedeni alır. |
then bir MotifError (MJX108) alır, ret bu hatanın cause'undadır; catch çalışmaz. |
MJX108bir dönüş değeridir:errorHandler'a bildirilmez, konsola yazılmaz. Değeri kullanmadan önceinstanceof Errordenetimi yazılır; denetim tutarsa bileşen yoktur,thenhiçbir şey yapmadan döner.finallyiki durumda da çalışır. İçinde bileşenin alanına dokunan kod, bileşen serbest bırakılmışsa boşaltılmış alana yazar; bu tür işlerithenvecatch'e koyun.then'in içinde fırlatılan hata zincirdeki sonrakicatch'e gider; bu, sözlerin olağan davranışıdır.thenmetodu olan herhangi bir nesne verilebilir.- Bileşenin yaşayıp yaşamadığına sonuç geldiği anda
isDisposedile bakılır; bayrak serbest bırakma bitincetrueolur (bkz. Denetimin zamanı).
Dönen sözün tipi Promise<T | MotifError>'dır. instanceof Error (ya da instanceof MotifError) denetiminden sonra değer T'ye daralır; denetimsiz kullanım derlenmez.
Bir örnek
Not sayfasındaki "Paylaş" düğmesi sunucudan bir paylaşım bağlantısı ister. İstek sürerken düğme kapalıdır; bağlantı gelince görünür, istek başarısız olursa bir uyarı çıkar. Kullanıcı yanıt gelmeden sayfadan ayrılırsa then MJX108 alır ve hiçbir şey yazmadan döner, catch çalışmaz:
import { Component, reactive } from "@motifx/core";
import type { Note } from "../services/notes";
type ShareLink = { url: string };
export class ShareButton extends Component<HTMLDivElement, { note: Note }> {
static override elementTag = "div";
state = reactive({ busy: false, url: "", failed: false });
share() {
this.state.busy = true;
this.state.failed = false;
this.doWork(fetch(`/api/notes/${this.props.note.id}/share`, { method: "POST" }).then((r) => r.json() as Promise<ShareLink>))
.then((link) => {
if (link instanceof Error) return;
this.state.url = link.url;
this.state.busy = false;
})
.catch(() => {
this.state.failed = true;
this.state.busy = false;
});
}
override onConfigured() {
this.class.add("share");
}
override view() {
return (
<>
<button type="button" disabled={() => this.state.busy} onclick={() => this.share()}>Paylaş</button>
<a x-display={() => this.state.url !== ""} href={() => this.state.url}>Paylaşım bağlantısı</a>
<small x-display={() => this.state.failed}>Paylaşılamadı.</small>
</>
);
}
}
import { Component, ComponentBase, motifComponent, motifFragment, reactive } from "@motifx/core";
import type { Note } from "../services/notes";
type ShareLink = { url: string };
export class ShareButton extends Component<HTMLDivElement, { note: Note }> {
static override elementTag = "div";
state = reactive({ busy: false, url: "", failed: false });
share() {
this.state.busy = true;
this.state.failed = false;
this.doWork(fetch(`/api/notes/${this.props.note.id}/share`, { method: "POST" }).then((r) => r.json() as Promise<ShareLink>))
.then((link) => {
if (link instanceof Error) return;
this.state.url = link.url;
this.state.busy = false;
})
.catch(() => {
this.state.failed = true;
this.state.busy = false;
});
}
override onConfigured() {
this.class.add("share");
}
override view() {
const button = motifComponent("button", (button: ComponentBase) => {
button.attr.add({ type: "button" });
button.bindings.add("disabled", () => this.state.busy);
button.motif.on("click", () => this.share());
button.controls.add(motifComponent("text", (node: ComponentBase) => node.setText("Paylaş")));
});
const link = motifComponent("a", {
preconfig: (a: ComponentBase) => a.bindings.display(() => this.state.url !== ""),
initializeComponent: (a: ComponentBase) => {
a.bindings.add("href", () => this.state.url);
a.setText("Paylaşım bağlantısı");
},
});
const failed = motifComponent("small", {
preconfig: (small: ComponentBase) => small.bindings.display(() => this.state.failed),
initializeComponent: (small: ComponentBase) => small.setText("Paylaşılamadı."),
});
return motifFragment({ childs: [button, link, failed] });
}
}
import { reactive } from "@motifx/core";
import type { Note } from "../services/notes";
type ShareLink = { url: string };
export function ShareButton(props: { note: Note }) {
const state = reactive({ busy: false, url: "", failed: false });
const share = () => {
state.busy = true;
state.failed = false;
box.doWork(fetch(`/api/notes/${props.note.id}/share`, { method: "POST" }).then((r) => r.json() as Promise<ShareLink>))
.then((link) => {
if (link instanceof Error) return;
state.url = link.url;
state.busy = false;
})
.catch(() => {
state.failed = true;
state.busy = false;
});
};
const box = (
<div class="share">
<button type="button" disabled={() => state.busy} onclick={() => share()}>Paylaş</button>
<a x-display={() => state.url !== ""} href={() => state.url}>Paylaşım bağlantısı</a>
<small x-display={() => state.failed}>Paylaşılamadı.</small>
</div>
);
return box;
}
import { ComponentBase, motifComponent, reactive } from "@motifx/core";
import type { Note } from "../services/notes";
type ShareLink = { url: string };
export function ShareButton(props: { note: Note }) {
const state = reactive({ busy: false, url: "", failed: false });
const share = () => {
state.busy = true;
state.failed = false;
box.doWork(fetch(`/api/notes/${props.note.id}/share`, { method: "POST" }).then((r) => r.json() as Promise<ShareLink>))
.then((link) => {
if (link instanceof Error) return;
state.url = link.url;
state.busy = false;
})
.catch(() => {
state.failed = true;
state.busy = false;
});
};
const box: ComponentBase = motifComponent("div", (div: ComponentBase) => {
div.class.add("share");
const button = motifComponent("button", (button: ComponentBase) => {
button.attr.add({ type: "button" });
button.bindings.add("disabled", () => state.busy);
button.motif.on("click", () => share());
button.controls.add(motifComponent("text", (node: ComponentBase) => node.setText("Paylaş")));
});
const link = motifComponent("a", {
preconfig: (a: ComponentBase) => a.bindings.display(() => state.url !== ""),
initializeComponent: (a: ComponentBase) => {
a.bindings.add("href", () => state.url);
a.setText("Paylaşım bağlantısı");
},
});
const failed = motifComponent("small", {
preconfig: (small: ComponentBase) => small.bindings.display(() => state.failed),
initializeComponent: (small: ComponentBase) => small.setText("Paylaşılamadı."),
});
div.controls.add(button, link, failed);
});
return box;
}
import { motifOptions, reactive } from "@motifx/core";
import type { Note } from "../services/notes";
type ShareLink = { url: string };
export const ShareButton = (props: { note: Note }) => motifOptions({
el: "div",
data: reactive({ busy: false, url: "", failed: false }),
share() {
const state = this.data;
state.busy = true;
state.failed = false;
this.doWork(fetch(`/api/notes/${props.note.id}/share`, { method: "POST" }).then((r) => r.json() as Promise<ShareLink>))
.then((link) => {
if (link instanceof Error) return;
state.url = link.url;
state.busy = false;
})
.catch(() => {
state.failed = true;
state.busy = false;
});
},
onConfigured() {
this.class.add("share");
},
view() {
const state = this.data;
return (
<>
<button type="button" disabled={() => state.busy} onclick={() => this.share()}>Paylaş</button>
<a x-display={() => state.url !== ""} href={() => state.url}>Paylaşım bağlantısı</a>
<small x-display={() => state.failed}>Paylaşılamadı.</small>
</>
);
},
});
import { ComponentBase, motifComponent, motifFragment, motifOptions, reactive } from "@motifx/core";
import type { Note } from "../services/notes";
type ShareLink = { url: string };
export const ShareButton = (props: { note: Note }) => motifOptions({
el: "div",
data: reactive({ busy: false, url: "", failed: false }),
share() {
const state = this.data;
state.busy = true;
state.failed = false;
this.doWork(fetch(`/api/notes/${props.note.id}/share`, { method: "POST" }).then((r) => r.json() as Promise<ShareLink>))
.then((link) => {
if (link instanceof Error) return;
state.url = link.url;
state.busy = false;
})
.catch(() => {
state.failed = true;
state.busy = false;
});
},
onConfigured() {
this.class.add("share");
},
view() {
const state = this.data;
const button = motifComponent("button", (button: ComponentBase) => {
button.attr.add({ type: "button" });
button.bindings.add("disabled", () => state.busy);
button.motif.on("click", () => this.share());
button.controls.add(motifComponent("text", (node: ComponentBase) => node.setText("Paylaş")));
});
const link = motifComponent("a", {
preconfig: (a: ComponentBase) => a.bindings.display(() => state.url !== ""),
initializeComponent: (a: ComponentBase) => {
a.bindings.add("href", () => state.url);
a.setText("Paylaşım bağlantısı");
},
});
const failed = motifComponent("small", {
preconfig: (small: ComponentBase) => small.bindings.display(() => state.failed),
initializeComponent: (small: ComponentBase) => small.setText("Paylaşılamadı."),
});
return motifFragment({ childs: [button, link, failed] });
},
});
import { Component, reactive } from "@motifx/core";
export class ShareButton extends Component {
static elementTag = "div";
state = reactive({ busy: false, url: "", failed: false });
share() {
this.state.busy = true;
this.state.failed = false;
this.doWork(fetch(`/api/notes/${this.props.note.id}/share`, { method: "POST" }).then((r) => r.json()))
.then((link) => {
if (link instanceof Error) return;
this.state.url = link.url;
this.state.busy = false;
})
.catch(() => {
this.state.failed = true;
this.state.busy = false;
});
}
onConfigured() {
this.class.add("share");
}
view() {
return (
<>
<button type="button" disabled={() => this.state.busy} onclick={() => this.share()}>Paylaş</button>
<a x-display={() => this.state.url !== ""} href={() => this.state.url}>Paylaşım bağlantısı</a>
<small x-display={() => this.state.failed}>Paylaşılamadı.</small>
</>
);
}
}
import { Component, motifComponent, motifFragment, reactive } from "@motifx/core";
export class ShareButton extends Component {
static elementTag = "div";
state = reactive({ busy: false, url: "", failed: false });
share() {
this.state.busy = true;
this.state.failed = false;
this.doWork(fetch(`/api/notes/${this.props.note.id}/share`, { method: "POST" }).then((r) => r.json()))
.then((link) => {
if (link instanceof Error) return;
this.state.url = link.url;
this.state.busy = false;
})
.catch(() => {
this.state.failed = true;
this.state.busy = false;
});
}
onConfigured() {
this.class.add("share");
}
view() {
const button = motifComponent("button", (button) => {
button.attr.add({ type: "button" });
button.bindings.add("disabled", () => this.state.busy);
button.motif.on("click", () => this.share());
button.controls.add(motifComponent("text", (node) => node.setText("Paylaş")));
});
const link = motifComponent("a", {
preconfig: (a) => a.bindings.display(() => this.state.url !== ""),
initializeComponent: (a) => {
a.bindings.add("href", () => this.state.url);
a.setText("Paylaşım bağlantısı");
},
});
const failed = motifComponent("small", {
preconfig: (small) => small.bindings.display(() => this.state.failed),
initializeComponent: (small) => small.setText("Paylaşılamadı."),
});
return motifFragment({ childs: [button, link, failed] });
}
}
import { reactive } from "@motifx/core";
export function ShareButton(props) {
const state = reactive({ busy: false, url: "", failed: false });
const share = () => {
state.busy = true;
state.failed = false;
box.doWork(fetch(`/api/notes/${props.note.id}/share`, { method: "POST" }).then((r) => r.json()))
.then((link) => {
if (link instanceof Error) return;
state.url = link.url;
state.busy = false;
})
.catch(() => {
state.failed = true;
state.busy = false;
});
};
const box = (
<div class="share">
<button type="button" disabled={() => state.busy} onclick={() => share()}>Paylaş</button>
<a x-display={() => state.url !== ""} href={() => state.url}>Paylaşım bağlantısı</a>
<small x-display={() => state.failed}>Paylaşılamadı.</small>
</div>
);
return box;
}
import { motifComponent, reactive } from "@motifx/core";
export function ShareButton(props) {
const state = reactive({ busy: false, url: "", failed: false });
const share = () => {
state.busy = true;
state.failed = false;
box.doWork(fetch(`/api/notes/${props.note.id}/share`, { method: "POST" }).then((r) => r.json()))
.then((link) => {
if (link instanceof Error) return;
state.url = link.url;
state.busy = false;
})
.catch(() => {
state.failed = true;
state.busy = false;
});
};
const box = motifComponent("div", (div) => {
div.class.add("share");
const button = motifComponent("button", (button) => {
button.attr.add({ type: "button" });
button.bindings.add("disabled", () => state.busy);
button.motif.on("click", () => share());
button.controls.add(motifComponent("text", (node) => node.setText("Paylaş")));
});
const link = motifComponent("a", {
preconfig: (a) => a.bindings.display(() => state.url !== ""),
initializeComponent: (a) => {
a.bindings.add("href", () => state.url);
a.setText("Paylaşım bağlantısı");
},
});
const failed = motifComponent("small", {
preconfig: (small) => small.bindings.display(() => state.failed),
initializeComponent: (small) => small.setText("Paylaşılamadı."),
});
div.controls.add(button, link, failed);
});
return box;
}
import { motifOptions, reactive } from "@motifx/core";
export const ShareButton = (props) => motifOptions({
el: "div",
data: reactive({ busy: false, url: "", failed: false }),
share() {
const state = this.data;
state.busy = true;
state.failed = false;
this.doWork(fetch(`/api/notes/${props.note.id}/share`, { method: "POST" }).then((r) => r.json()))
.then((link) => {
if (link instanceof Error) return;
state.url = link.url;
state.busy = false;
})
.catch(() => {
state.failed = true;
state.busy = false;
});
},
onConfigured() {
this.class.add("share");
},
view() {
const state = this.data;
return (
<>
<button type="button" disabled={() => state.busy} onclick={() => this.share()}>Paylaş</button>
<a x-display={() => state.url !== ""} href={() => state.url}>Paylaşım bağlantısı</a>
<small x-display={() => state.failed}>Paylaşılamadı.</small>
</>
);
},
});
import { motifComponent, motifFragment, motifOptions, reactive } from "@motifx/core";
export const ShareButton = (props) => motifOptions({
el: "div",
data: reactive({ busy: false, url: "", failed: false }),
share() {
const state = this.data;
state.busy = true;
state.failed = false;
this.doWork(fetch(`/api/notes/${props.note.id}/share`, { method: "POST" }).then((r) => r.json()))
.then((link) => {
if (link instanceof Error) return;
state.url = link.url;
state.busy = false;
})
.catch(() => {
state.failed = true;
state.busy = false;
});
},
onConfigured() {
this.class.add("share");
},
view() {
const state = this.data;
const button = motifComponent("button", (button) => {
button.attr.add({ type: "button" });
button.bindings.add("disabled", () => state.busy);
button.motif.on("click", () => this.share());
button.controls.add(motifComponent("text", (node) => node.setText("Paylaş")));
});
const link = motifComponent("a", {
preconfig: (a) => a.bindings.display(() => state.url !== ""),
initializeComponent: (a) => {
a.bindings.add("href", () => state.url);
a.setText("Paylaşım bağlantısı");
},
});
const failed = motifComponent("small", {
preconfig: (small) => small.bindings.display(() => state.failed),
initializeComponent: (small) => small.setText("Paylaşılamadı."),
});
return motifFragment({ childs: [button, link, failed] });
},
});
share() bileşenin bir metodudur; doWork da öyle, this.doWork(…) olarak çağrılır.
Fonksiyon bileşeninde this yoktur; doWork dönen kökün metodudur. share kökü box adıyla kullanır; düğmeye tıklandığında kök çoktan kurulmuştur.
Not sayfası düğmeyi açık notla yerleştirir: <ShareButton note={note} />.
Not sayfası düğmeyi açık notla yerleştirir: new ShareButton({ note }).
Not sayfası düğmeyi açık notla yerleştirir: motifComponent(ShareButton, { note }).
using'den farkı
doWork sonucu bir söz olarak geri verir; using ise sonucu kendisine verilen işleyicilerde bitirir ve hiçbir şey döndürmez. Aşağıdaki ayrıntıların hepsi bu farktan doğar:
doWork |
using |
|
|---|---|---|
| Döndürdüğü | Bir söz; then, catch, finally eklenir, çağırana döndürülebilir. |
Hiçbir şey; iş, çağrıda verilen işleyicilerde biter. |
| Gelen değer | then ya değeri ya MJX108'i alır; değeri kullanmadan önce instanceof Error denetimi gerekir. |
onfulfilled değerin kendisini alır; denetim gerekmez. |
| Serbest bırakıldıktan sonra | then MJX108 alır, catch çalışmaz, finally çalışır. |
Hiçbir işleyici çalışmaz. |
| Bileşen yaşarken ret | catch'e gider; catch yoksa yakalanmamış söz reddi olur. |
onrejected'a gider; o yoksa sessizce yutulur. |
| İşleyicinin fırlattığı hata | Zincirdeki sonraki catch'e gider; o yoksa yakalanmamış söz reddi olur. |
MJX129 ile errorHandler'a bildirilir. |
Hangisini ne zaman
doWork şu durumlarda seçilir:
- Bir işin ardından başka bir iş gelecekse; örneğin notu kaydedip ardından listeyi yeniden yüklemek.
thenikinci isteği yinedoWorkile başlatıp döndürür; ilk adımdaMJX108gelirsetheninstanceof Errordenetimiyle erken döner ve ikinci istek hiç gitmez. Zincirin sonundaki tekcatchiki adımın reddini de yakalar. - Birden çok isteğin birlikte bitmesi bekleniyorsa:
doWork(Promise.all([…]))sonuçları bir dizi olarak verir; bileşen serbest bırakılmışsa tek birMJX108gelir. - İşi başlatan metot sözü döndürecekse; üst bileşen, bir kısayol tuşu ya da bir test işin bitmesini bekleyebilir.
finallyya da zincir boyunca taşınan bir hata gerekiyorsa.finallybileşen serbest bırakıldıktan sonra da çalışır.
Sonuç yalnız bu bileşenin durumuna yazılacaksa ve işin bitişini kimse beklemiyorsa using daha kısadır: instanceof Error denetimi ve catch gerekmez, işleyicinin fırlattığı hata errorHandler'a kendiliğinden gider.
doWork de isteği durdurmaz; bileşen serbest bırakılınca isteği iptal etmek için İstek sürer bölümüne bakın.
Sonraki adım
Bileşenlerin görünümünü yazmanın ayrıntıları için JSX ve Şablonlar bölümüne geçin.