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.

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:

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.