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.

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:

Şu durumlarda doWork gerekir:

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.