Temel kavramlar
using ile Asenkron Sonuç
Bileşen serbest bırakıldıktan sonra gelen sonucu atan `using`: çağrı biçimi, işleyiciler, MJX129 ve denetimin zamanı.
Bir bileşen sunucudan veri istediğinde yanıtın ne zaman geleceği belli değildir; yanıt gelene kadar bileşen serbest bırakılmış olabilir. Serbest bırakılan bileşenin bağları durur, dispose() çağrılan bileşenin alanları da boşaltılır (bkz. Kendiliğinden bırakılanlar). Geç gelen yanıtı bu bileşene yazan kod hiçbir şey göstermez, alan boşaltılmışsa TypeError fırlatır. using(söz, onfulfilled?, onrejected?) sözün sonucunu yalnız bileşen yaşıyorsa işleyicilere verir; bileşen serbest bırakılmışsa sonucu atar.
Çağrı biçimi
| Parametre | Ne alır |
|---|---|
söz |
Bir Promise ya da then metodu olan herhangi bir nesne. |
onfulfilled(değer) |
Söz çözülünce, bileşen yaşıyorsa çağrılır. |
onrejected(neden) |
Söz reddedilince, bileşen yaşıyorsa çağrılır. |
- Bileşen serbest bırakılmışsa iki işleyici de çağrılmaz; sonuç da ret de atılır.
onrejectedverilmezse ret sessizce yutulur: neerrorHandler'a gider ne yakalanmamış söz reddi olarak görünür. Hatayı görmek istiyorsanızonrejectedyazın.usingdeğer döndürmez. Sonucu beklemek, zincirlemek ya da çağırana geri vermek gerekiyorsa doWork kullanılır.- İşleyicinin döndürdüğü değer kullanılmaz. İşleyici hata fırlatırsa ya da
asyncbir işleyicinin sözü reddedilirse hataMJX129koduylaerrorHandler'a bildirilir; özgün hatacause'dadır (bkz. Hata Yönetimi).
Değerin tipi sözden gelir: Promise<SharedNote[]> veren bir sözde onfulfilled'ın parametresi SharedNote[]'dir. r.json() gibi any döndüren bir sözde tip kaybolur; aşağıdaki örnekteki gibi as Promise<SharedNote[]> ile ya da parametreye tip yazarak verilir. İşleyiciler gövdeli de yazılabilir, bir değer döndürmeleri gerekmez.
Bir örnek
Kenar çubuğundaki "benimle paylaşılanlar" bölümü kurulurken sunucudan listeyi ister. Yanıt gelince liste dolar, istek başarısız olursa bir uyarı görünür. Kullanıcı yanıt gelmeden başka sayfaya geçer ve bölüm serbest bırakılırsa iki işleyici de çalışmaz:
import { Component, reactive } from "@motifx/core";
type SharedNote = { id: number; title: string; owner: string };
export class SharedNotes extends Component {
static override elementTag = "section";
state = reactive({ items: [] as SharedNote[], loading: true, failed: false });
override onConfigured() {
this.class.add("shared");
this.using(
fetch("/api/notes/shared").then((r) => r.json() as Promise<SharedNote[]>),
(items) => {
this.state.items = items;
this.state.loading = false;
},
() => {
this.state.failed = true;
this.state.loading = false;
},
);
}
override view() {
return (
<>
<h2>Benimle paylaşılanlar</h2>
<p x-display={() => this.state.loading}>Yükleniyor…</p>
<p x-display={() => this.state.failed}>Paylaşılan notlar alınamadı.</p>
<ul>
{this.state.items.map((note) => (
<li key={note.id}>
<span>{note.title}</span>
<small>{note.owner}</small>
</li>
))}
</ul>
</>
);
}
}
import { Component, ComponentBase, motifComponent, motifFragment, reactive } from "@motifx/core";
type SharedNote = { id: number; title: string; owner: string };
export class SharedNotes extends Component {
static override elementTag = "section";
state = reactive({ items: [] as SharedNote[], loading: true, failed: false });
override onConfigured() {
this.class.add("shared");
this.using(
fetch("/api/notes/shared").then((r) => r.json() as Promise<SharedNote[]>),
(items) => {
this.state.items = items;
this.state.loading = false;
},
() => {
this.state.failed = true;
this.state.loading = false;
},
);
}
override view() {
const title = motifComponent("h2", (h2: ComponentBase) => h2.setText("Benimle paylaşılanlar"));
const loading = motifComponent("p", {
preconfig: (p: ComponentBase) => p.bindings.display(() => this.state.loading),
initializeComponent: (p: ComponentBase) => p.setText("Yükleniyor…"),
});
const failed = motifComponent("p", {
preconfig: (p: ComponentBase) => p.bindings.display(() => this.state.failed),
initializeComponent: (p: ComponentBase) => p.setText("Paylaşılan notlar alınamadı."),
});
const list = motifComponent("ul", (ul: ComponentBase) => {
ul.controls.add(motifComponent({
initializeComponent: (holder: ComponentBase) => holder.bindings.list(() => this.state.items, (note: SharedNote) => this.row(note)),
}));
});
return motifFragment({ childs: [title, loading, failed, list] });
}
private row(note: SharedNote) {
return motifComponent("li", {
indexkey: () => note.id,
initializeComponent: (li: ComponentBase) => {
li.controls.add(
motifComponent("span", (span: ComponentBase) => span.setText(note.title)),
motifComponent("small", (small: ComponentBase) => small.setText(note.owner)),
);
},
});
}
}
import { reactive } from "@motifx/core";
type SharedNote = { id: number; title: string; owner: string };
export function SharedNotes() {
const state = reactive({ items: [] as SharedNote[], loading: true, failed: false });
const section = (
<section class="shared">
<h2>Benimle paylaşılanlar</h2>
<p x-display={() => state.loading}>Yükleniyor…</p>
<p x-display={() => state.failed}>Paylaşılan notlar alınamadı.</p>
<ul>
{state.items.map((note) => (
<li key={note.id}>
<span>{note.title}</span>
<small>{note.owner}</small>
</li>
))}
</ul>
</section>
);
section.using(
fetch("/api/notes/shared").then((r) => r.json() as Promise<SharedNote[]>),
(items) => {
state.items = items;
state.loading = false;
},
() => {
state.failed = true;
state.loading = false;
},
);
return section;
}
import { ComponentBase, motifComponent, reactive } from "@motifx/core";
type SharedNote = { id: number; title: string; owner: string };
export function SharedNotes() {
const state = reactive({ items: [] as SharedNote[], loading: true, failed: false });
const row = (note: SharedNote) =>
motifComponent("li", {
indexkey: () => note.id,
initializeComponent: (li: ComponentBase) => {
li.controls.add(
motifComponent("span", (span: ComponentBase) => span.setText(note.title)),
motifComponent("small", (small: ComponentBase) => small.setText(note.owner)),
);
},
});
const section: ComponentBase = motifComponent("section", (root: ComponentBase) => {
root.class.add("shared");
const title = motifComponent("h2", (h2: ComponentBase) => h2.setText("Benimle paylaşılanlar"));
const loading = motifComponent("p", {
preconfig: (p: ComponentBase) => p.bindings.display(() => state.loading),
initializeComponent: (p: ComponentBase) => p.setText("Yükleniyor…"),
});
const failed = motifComponent("p", {
preconfig: (p: ComponentBase) => p.bindings.display(() => state.failed),
initializeComponent: (p: ComponentBase) => p.setText("Paylaşılan notlar alınamadı."),
});
const list = motifComponent("ul", (ul: ComponentBase) => {
ul.controls.add(motifComponent({
initializeComponent: (holder: ComponentBase) => holder.bindings.list(() => state.items, (note: SharedNote) => row(note)),
}));
});
root.controls.add(title, loading, failed, list);
});
section.using(
fetch("/api/notes/shared").then((r) => r.json() as Promise<SharedNote[]>),
(items) => {
state.items = items;
state.loading = false;
},
() => {
state.failed = true;
state.loading = false;
},
);
return section;
}
import { motifOptions, reactive } from "@motifx/core";
type SharedNote = { id: number; title: string; owner: string };
export const SharedNotes = () => motifOptions({
el: "section",
data: reactive({ items: [] as SharedNote[], loading: true, failed: false }),
onConfigured() {
const state = this.data;
this.class.add("shared");
this.using(
fetch("/api/notes/shared").then((r) => r.json() as Promise<SharedNote[]>),
(items) => {
state.items = items;
state.loading = false;
},
() => {
state.failed = true;
state.loading = false;
},
);
},
view() {
const state = this.data;
return (
<>
<h2>Benimle paylaşılanlar</h2>
<p x-display={() => state.loading}>Yükleniyor…</p>
<p x-display={() => state.failed}>Paylaşılan notlar alınamadı.</p>
<ul>
{state.items.map((note) => (
<li key={note.id}>
<span>{note.title}</span>
<small>{note.owner}</small>
</li>
))}
</ul>
</>
);
},
});
import { ComponentBase, motifComponent, motifFragment, motifOptions, reactive } from "@motifx/core";
type SharedNote = { id: number; title: string; owner: string };
export const SharedNotes = () => motifOptions({
el: "section",
data: reactive({ items: [] as SharedNote[], loading: true, failed: false }),
onConfigured() {
const state = this.data;
this.class.add("shared");
this.using(
fetch("/api/notes/shared").then((r) => r.json() as Promise<SharedNote[]>),
(items) => {
state.items = items;
state.loading = false;
},
() => {
state.failed = true;
state.loading = false;
},
);
},
view() {
const state = this.data;
const row = (note: SharedNote) =>
motifComponent("li", {
indexkey: () => note.id,
initializeComponent: (li: ComponentBase) => {
li.controls.add(
motifComponent("span", (span: ComponentBase) => span.setText(note.title)),
motifComponent("small", (small: ComponentBase) => small.setText(note.owner)),
);
},
});
const title = motifComponent("h2", (h2: ComponentBase) => h2.setText("Benimle paylaşılanlar"));
const loading = motifComponent("p", {
preconfig: (p: ComponentBase) => p.bindings.display(() => state.loading),
initializeComponent: (p: ComponentBase) => p.setText("Yükleniyor…"),
});
const failed = motifComponent("p", {
preconfig: (p: ComponentBase) => p.bindings.display(() => state.failed),
initializeComponent: (p: ComponentBase) => p.setText("Paylaşılan notlar alınamadı."),
});
const list = motifComponent("ul", (ul: ComponentBase) => {
ul.controls.add(motifComponent({
initializeComponent: (holder: ComponentBase) => holder.bindings.list(() => state.items, (note: SharedNote) => row(note)),
}));
});
return motifFragment({ childs: [title, loading, failed, list] });
},
});
import { Component, reactive } from "@motifx/core";
export class SharedNotes extends Component {
static elementTag = "section";
state = reactive({ items: [], loading: true, failed: false });
onConfigured() {
this.class.add("shared");
this.using(
fetch("/api/notes/shared").then((r) => r.json()),
(items) => {
this.state.items = items;
this.state.loading = false;
},
() => {
this.state.failed = true;
this.state.loading = false;
},
);
}
view() {
return (
<>
<h2>Benimle paylaşılanlar</h2>
<p x-display={() => this.state.loading}>Yükleniyor…</p>
<p x-display={() => this.state.failed}>Paylaşılan notlar alınamadı.</p>
<ul>
{this.state.items.map((note) => (
<li key={note.id}>
<span>{note.title}</span>
<small>{note.owner}</small>
</li>
))}
</ul>
</>
);
}
}
import { Component, motifComponent, motifFragment, reactive } from "@motifx/core";
export class SharedNotes extends Component {
static elementTag = "section";
state = reactive({ items: [], loading: true, failed: false });
onConfigured() {
this.class.add("shared");
this.using(
fetch("/api/notes/shared").then((r) => r.json()),
(items) => {
this.state.items = items;
this.state.loading = false;
},
() => {
this.state.failed = true;
this.state.loading = false;
},
);
}
view() {
const title = motifComponent("h2", (h2) => h2.setText("Benimle paylaşılanlar"));
const loading = motifComponent("p", {
preconfig: (p) => p.bindings.display(() => this.state.loading),
initializeComponent: (p) => p.setText("Yükleniyor…"),
});
const failed = motifComponent("p", {
preconfig: (p) => p.bindings.display(() => this.state.failed),
initializeComponent: (p) => p.setText("Paylaşılan notlar alınamadı."),
});
const list = motifComponent("ul", (ul) => {
ul.controls.add(motifComponent({
initializeComponent: (holder) => holder.bindings.list(() => this.state.items, (note) => this.row(note)),
}));
});
return motifFragment({ childs: [title, loading, failed, list] });
}
row(note) {
return motifComponent("li", {
indexkey: () => note.id,
initializeComponent: (li) => {
li.controls.add(
motifComponent("span", (span) => span.setText(note.title)),
motifComponent("small", (small) => small.setText(note.owner)),
);
},
});
}
}
import { reactive } from "@motifx/core";
export function SharedNotes() {
const state = reactive({ items: [], loading: true, failed: false });
const section = (
<section class="shared">
<h2>Benimle paylaşılanlar</h2>
<p x-display={() => state.loading}>Yükleniyor…</p>
<p x-display={() => state.failed}>Paylaşılan notlar alınamadı.</p>
<ul>
{state.items.map((note) => (
<li key={note.id}>
<span>{note.title}</span>
<small>{note.owner}</small>
</li>
))}
</ul>
</section>
);
section.using(
fetch("/api/notes/shared").then((r) => r.json()),
(items) => {
state.items = items;
state.loading = false;
},
() => {
state.failed = true;
state.loading = false;
},
);
return section;
}
import { motifComponent, reactive } from "@motifx/core";
export function SharedNotes() {
const state = reactive({ items: [], loading: true, failed: false });
const row = (note) =>
motifComponent("li", {
indexkey: () => note.id,
initializeComponent: (li) => {
li.controls.add(
motifComponent("span", (span) => span.setText(note.title)),
motifComponent("small", (small) => small.setText(note.owner)),
);
},
});
const section = motifComponent("section", (root) => {
root.class.add("shared");
const title = motifComponent("h2", (h2) => h2.setText("Benimle paylaşılanlar"));
const loading = motifComponent("p", {
preconfig: (p) => p.bindings.display(() => state.loading),
initializeComponent: (p) => p.setText("Yükleniyor…"),
});
const failed = motifComponent("p", {
preconfig: (p) => p.bindings.display(() => state.failed),
initializeComponent: (p) => p.setText("Paylaşılan notlar alınamadı."),
});
const list = motifComponent("ul", (ul) => {
ul.controls.add(motifComponent({
initializeComponent: (holder) => holder.bindings.list(() => state.items, (note) => row(note)),
}));
});
root.controls.add(title, loading, failed, list);
});
section.using(
fetch("/api/notes/shared").then((r) => r.json()),
(items) => {
state.items = items;
state.loading = false;
},
() => {
state.failed = true;
state.loading = false;
},
);
return section;
}
import { motifOptions, reactive } from "@motifx/core";
export const SharedNotes = () => motifOptions({
el: "section",
data: reactive({ items: [], loading: true, failed: false }),
onConfigured() {
const state = this.data;
this.class.add("shared");
this.using(
fetch("/api/notes/shared").then((r) => r.json()),
(items) => {
state.items = items;
state.loading = false;
},
() => {
state.failed = true;
state.loading = false;
},
);
},
view() {
const state = this.data;
return (
<>
<h2>Benimle paylaşılanlar</h2>
<p x-display={() => state.loading}>Yükleniyor…</p>
<p x-display={() => state.failed}>Paylaşılan notlar alınamadı.</p>
<ul>
{state.items.map((note) => (
<li key={note.id}>
<span>{note.title}</span>
<small>{note.owner}</small>
</li>
))}
</ul>
</>
);
},
});
import { motifComponent, motifFragment, motifOptions, reactive } from "@motifx/core";
export const SharedNotes = () => motifOptions({
el: "section",
data: reactive({ items: [], loading: true, failed: false }),
onConfigured() {
const state = this.data;
this.class.add("shared");
this.using(
fetch("/api/notes/shared").then((r) => r.json()),
(items) => {
state.items = items;
state.loading = false;
},
() => {
state.failed = true;
state.loading = false;
},
);
},
view() {
const state = this.data;
const row = (note) =>
motifComponent("li", {
indexkey: () => note.id,
initializeComponent: (li) => {
li.controls.add(
motifComponent("span", (span) => span.setText(note.title)),
motifComponent("small", (small) => small.setText(note.owner)),
);
},
});
const title = motifComponent("h2", (h2) => h2.setText("Benimle paylaşılanlar"));
const loading = motifComponent("p", {
preconfig: (p) => p.bindings.display(() => state.loading),
initializeComponent: (p) => p.setText("Yükleniyor…"),
});
const failed = motifComponent("p", {
preconfig: (p) => p.bindings.display(() => state.failed),
initializeComponent: (p) => p.setText("Paylaşılan notlar alınamadı."),
});
const list = motifComponent("ul", (ul) => {
ul.controls.add(motifComponent({
initializeComponent: (holder) => holder.bindings.list(() => state.items, (note) => row(note)),
}));
});
return motifFragment({ childs: [title, loading, failed, list] });
},
});
İstek onConfigured'da başlar: bileşenin ayarları hazırdır, elemanın belgede olması gerekmez. using bileşenin bir metodudur, this.using(…) olarak çağrılır.
Fonksiyon bileşeninde this yoktur; using dönen kökün metodudur. Gövde kökü kurar, section.using(…) ile isteği bağlar ve kökü döndürür. Gövde bileşen kurulurken bir kez çalıştığı için istek de bir kez gider.
Kenar çubuğu bölümü <SharedNotes /> ile yerleştirir.
Kenar çubuğu bölümü new SharedNotes() ile yerleştirir.
Kenar çubuğu bölümü motifComponent(SharedNotes) ile yerleştirir.
doWork'ten farkı
using sonucu kendisine verilen işleyicilerde bitirir; doWork ise sonucu bir söz olarak geri verir. Aşağıdaki ayrıntıların hepsi bu farktan doğar:
using |
doWork |
|
|---|---|---|
| Döndürdüğü | Hiçbir şey; iş, çağrıda verilen işleyicilerde biter. | Bir söz; then, catch, finally eklenir, çağırana döndürülebilir. |
| Gelen değer | onfulfilled değerin kendisini alır; denetim gerekmez. |
then ya değeri ya MJX108'i alır; değeri kullanmadan önce instanceof Error denetimi gerekir. |
| Serbest bırakıldıktan sonra | Hiçbir işleyici çalışmaz. | then MJX108 alır, catch çalışmaz, finally çalışır. |
| Bileşen yaşarken ret | onrejected'a gider; o yoksa sessizce yutulur. |
catch'e gider; catch yoksa yakalanmamış söz reddi olur. |
| İşleyicinin fırlattığı hata | MJX129 ile errorHandler'a bildirilir. |
Zincirdeki sonraki catch'e gider; o yoksa yakalanmamış söz reddi olur. |
Hangisini ne zaman
using şu durumlarda yeter:
- Sonuç yalnız bu bileşenin durumuna yazılacaksa: açılışta bir liste yüklemek, arka planda tazelemek, bir önizlemenin içeriğini getirmek.
- İşin ne zaman bittiğini bileşenin dışında kimsenin bilmesi gerekmiyorsa.
- Başarı ve başarısızlık için birer işleyici yetiyorsa. Bir hatayı bilerek görmezden gelmek de
onrejected'ı yazmamakla olur.
Şu durumlarda doWork gerekir:
- Bir işin ardından başka bir iş gelecekse; örneğin notu kaydedip ardından listeyi yeniden yüklemek.
- Birden çok isteğin birlikte bitmesi bekleniyorsa:
doWork(Promise.all([…])). - İş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.
Denetimin zamanı
using bileşenin yaşayıp yaşamadığına sonuç geldiği anda, isDisposed bayrağıyla bakar. Bu bayrak dispose() çağrıldığı anda değil, serbest bırakma bitince true olur. Serbest bırakma sürerken gelen sonuç, örneğin dispose()'tan hemen önce çözülmüş bir sözün sonucu, hâlâ işlenir; bileşenin bağları o sırada durmuş olduğundan ekranda bir şey değişmez.
Gizlenen (motif.hide(), x-display), ağaçtan ayrılan (controls.detach) ya da başka yere taşınan bileşen serbest bırakılmış sayılmaz; sonucu işlenir.
İstek sürer
using sözü durdurmaz: bileşen serbest bırakılsa da sunucuya giden istek sürer, yalnız sonucu atılır. İsteği de durdurmak gerekiyorsa bir AbortController kurun, signal'ini fetch'e verin ve abort() çağrısını motif.setDisposable(() => controller.abort()) ile bileşenin ömrüne bağlayın (bkz. Elle bağlanan kaynaklar). setDisposable işleyicisi isDisposed true olduktan sonra çalışır. Bu yüzden iptal edilen isteğin AbortError reddini using de atar, onrejected çağrılmaz. doWork aynı durumda MJX108 verir; AbortError onun cause'undadır.
Sonraki adım
Sonucu bekleyen ya da zincirleyen işler için doWork ile Asenkron Sonuç bölümüne geçin.