React: 'isMounted' ์ง€์› ์ค‘๋‹จ

์— ๋งŒ๋“  2015๋…„ 11์›” 14์ผ  ยท  48์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: facebook/react

isMounted ๋Š” ์ด๋ฏธ ES6 ํด๋ž˜์Šค์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์œผ๋ฉฐ ์ด๋ฏธ "์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค"๋ผ๋Š” ๊ฒฝ๊ณ ๊ฐ€ ์žˆ์ง€๋งŒ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” github ๋ฌธ์ œ๋Š” ์—†์Šต๋‹ˆ๋‹ค. ์˜ค๋Š˜ ๋…ผ์˜์— ๋”ฐ๋ฅด๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ isMounted ์—์„œ ๋ฉ€์–ด์ง€๊ธฐ ์‹œ์ž‘ํ•˜๊ณ  ๋” ์ด์ƒ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๋ฐ ๋™์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์—ฌ์ „ํžˆ ํ”„๋ผ๋ฏธ์Šค(๋ฐ ๊ด€๋ จ ์‚ฌ์šฉ ์‚ฌ๋ก€)์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ์ข‹์€ ์ด์•ผ๊ธฐ๋ฅผ ํŒŒ์•…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๋Š” ํ•ด๋‹น ๋ชฉํ‘œ๋ฅผ ํ–ฅํ•œ ์ง„ํ–‰ ์ƒํ™ฉ์„ ์ถ”์ ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ฐฐ๊ฒฝ์€ ๋‹ค์Œ์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

์ด ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์€ ๋ชจ๋“  ์•ฝ์†์— ์ทจ์†Œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then((val) =>
      hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    );
    promise.catch((error) =>
      hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

ํŽธ์ง‘: ์ •ํ™•์„ฑ/์™„์ „์„ฑ์„ ์œ„ํ•ด ์—…๋ฐ์ดํŠธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

const somePromise = new Promise(r => setTimeout(r, 1000));

const cancelable = makeCancelable(somePromise);

cancelable
  .promise
  .then(() => console.log('resolved'))
  .catch(({isCanceled, ...error}) => console.log('isCanceled', isCanceled));

// Cancel promise
cancelable.cancel();

๋ชจ๋“  48 ๋Œ“๊ธ€

๋‚˜๋Š” ์ด๊ฒƒ์— ๋™์˜ํ•˜์ง€ ์•Š๋Š”๋‹ค. ํŠนํžˆ ES6 ์•ฝ์†์€ componentWillUnmount ์—์„œ ์•ˆ์ •์ ์œผ๋กœ ์ทจ์†Œ๋  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ setState ๋˜๋Š” ๋‹ค๋ฅธ ์ž‘์—… ์ „์— ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋งˆ์šดํŠธ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์„ ์ œ๊ฑฐํ•˜๋ฉด ์ถ”์ ํ•˜๊ธฐ ์–ด๋ ค์šด ๋น„๋™๊ธฐ ๋ฒ„๊ทธ๊ฐ€ ๋งŽ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

@yaycmyk ๋”ฐ๋ผ์„œ ๋ผ์ธ:

์šฐ๋ฆฌ๋Š” ์—ฌ์ „ํžˆ ํ”„๋ผ๋ฏธ์Šค(๋ฐ ๊ด€๋ จ ์‚ฌ์šฉ ์‚ฌ๋ก€)์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ์ข‹์€ ์ด์•ผ๊ธฐ๋ฅผ ํŒŒ์•…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํŠนํžˆ ๋‚ด๊ฐ€ ๋‚˜์—ดํ•œ ๋ฐฐ๊ฒฝ ๋ฌธ์ œ๋ฅผ ์ฝ์œผ์‹ญ์‹œ์˜ค. https://github.com/facebook/react/issues/2787#issuecomment -68738793

๋Œ“๊ธ€ ์ž˜ ์ฝ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋‹จ์ง€ ๋ฌธ์ œ๋ฅผ ๋‹ค๋ฃจ๊ธฐ ์–ด๋ ต๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์™œ ์•ฝ์†์„ ์ทจ์†Œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๊นŒ? ๋ชจ๋“  ์ถœ์ฒ˜/์ฆ๋ช…/์˜ˆ์‹œ?

2015๋…„ 11์›” 16์ผ ์›”์š”์ผ์— Evan Jacobs [email protected] ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ผ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์— ๋™์˜ํ•˜์ง€ ์•Š๋Š”๋‹ค. ํŠนํžˆ ES6 ์•ฝ์†์€ ์‹ ๋ขฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
componentWillUnmount์—์„œ ์ทจ์†Œ๋˜์—ˆ์œผ๋ฏ€๋กœ ํ™•์ธํ•˜๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
๊ตฌ์„ฑ ์š”์†Œ๋Š” setState ๋˜๋Š” ๋‹ค๋ฅธ ์ž‘์—…์ด ์—ด๊ธฐ ์ „์— ๋งˆ์šดํŠธ๋ฉ๋‹ˆ๋‹ค.
๋งŽ์€ ๋น„๋™๊ธฐ ๋ฒ„๊ทธ๋ฅผ ์ถ”์ ํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

@nvartolomei ES6 Promise ์‚ฌ์–‘์„ ์‚ดํŽด๋ณด์„ธ์š”.

์ด๊ฒƒ์€ ๋‹น์žฅ์— ์ผ์–ด๋‚˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์žฅ๊ธฐ์ ์ธ ๋ชฉํ‘œ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ๋Š” ๊ณ„ํš๊ณผ ๋…ผ์˜๋ฅผ ํ•œ ๊ณณ์—์„œ ์ถ”์ ํ•˜๊ณ  ๋ชจ๋“  ๋ฌธ์ œ์— ๋Œ€ํ•œ ์˜๊ฒฌ์„ ๊ณต์œ ํ•˜์ง€ ์•Š๊ธฐ๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ํ˜„์žฌ ์ทจ์†Œํ•  ์ˆ˜ ์—†๋Š” Promise์˜ ๋ฌธ์ œ๋ฅผ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์šฐ๋ฆฌ๊ฐ€ ์•„์ง ์ด๊ฒƒ์„ ํ•˜์ง€ ์•Š์€ ์ฃผ๋œ ์ด์œ ์ž…๋‹ˆ๋‹ค.

@yaycmyk ๋งค์šฐ ๋ณต์žกํ•œ ๋ฌธ์ œ๋ฅผ ๊ณผ๋„ํ•˜๊ฒŒ ๋‹จ์ˆœํ™”ํ•˜๊ธฐ ์œ„ํ•ด ... ์˜๊ฒฌ์— ๋”ฐ๋ฅด๋ฉด ... ๋งˆ์šดํŠธ๋˜์ง€ ์•Š์€ ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•ด setState ๋ฅผ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด isMounted ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์‹ค์ œ๋กœ setState ๊ฒฝ๊ณ ๋Š” ํ‘œ์‹œํ•˜๋ ค๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ๋Š” ๋ฌธ์ œ๋ฅผ ์ˆจ๊ธธ ๋ฟ์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ Promise์˜ ๊ฒฐ๊ณผ๋กœ setState ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์€ ํ…Œ์ŠคํŠธ์—์„œ ๋ฐ˜๋“œ์‹œ ๋‚˜ํƒ€๋‚˜์ง€ ์•Š๋Š” ๊ฒฝ์Ÿ ์กฐ๊ฑด์„ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์–ด์จŒ๋“  ์•ฝ๊ฐ„์˜ ์•ˆํ‹ฐ ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์šฐ๋ฆฌ๋Š” ๊ทธ๊ฒƒ์„ ์—†์• ๊ณ  React์—์„œ promise๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ "๋ชจ๋ฒ” ์‚ฌ๋ก€" ๊ถŒ์žฅ ์‚ฌํ•ญ์„ ํŒŒ์•…ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ๊ฐ€ ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ต๋‹ค๋Š” ๋ฐ๋Š” ๋™์˜ํ•˜์ง€๋งŒ, ์ด๋Š” ์šฐ๋ฆฌ๊ฐ€ ์•„์ง ํŒŒ์•…ํ•˜๊ณ  ์žˆ๊ณ  ์•„์ง ๋ฏธ๋ฆฌ ์ค€๋น„๋œ ๋‹ต๋ณ€์ด ์—†๋Š” ๋ณต์žกํ•œ ๋ฌธ์ œ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

Promise์˜ ๊ฒฐ๊ณผ๋กœ setState๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์€ ์–ด์จŒ๋“  ์•ฝ๊ฐ„์˜ ์•ˆํ‹ฐ ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ์—์„œ ๋ฐ˜๋“œ์‹œ ๋‚˜ํƒ€๋‚˜์ง€ ์•Š์„ ๊ฒฝ์Ÿ ์กฐ๊ฑด์„ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ์ด์— ๋™์˜ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฝ˜ํ…์ธ ๋ฅผ ๋น„๋™๊ธฐ์‹์œผ๋กœ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๊ณ  ํ•ด๊ฒฐ๋˜๋ฉด ํ•ด๋‹น ์ฝ˜ํ…์ธ ๋ฅผ ํŒ์—…ํ•˜๊ธฐ ์œ„ํ•ด ์ „์ฒด ๊ทœ๋ชจ์˜ ์žฌ๋ Œ๋”๋ง์„ ๊ฑฐ์น˜๊ณ  ์‹ถ์ง€ ์•Š์„ ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ „์ฒด ๊ฐ€์ƒ ๋ Œ๋”๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์€ ๋ฌดํ•œ ํ…Œ์ด๋ธ” ๋ทฐ ๊ตฌํ˜„์—์„œ ํŠนํžˆ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์•ฝ์†์„ ์ทจ์†Œํ•  ์ˆ˜ ์—†์„ ์ˆ˜๋„ ์žˆ์ง€๋งŒ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งˆ์šดํŠธ ํ•ด์ œ ์‹œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์—ญ์ฐธ์กฐํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const SomeComponent = React.createClass({
    componentDidMount() {
        this.protect = protectFromUnmount();

        ajax(/* */).then(
            this.protect( // <-- barrier between the promise and the component
                response => {this.setState({thing: response.thing});}
            )
        );
    },
    componentWillUnmount() {
        this.protect.unmount();
    },
});

์ค‘์š”ํ•œ ์ฐจ์ด์ ์€ this.protect.unmount() ๊ฐ€ componentWillUnmount ์—์„œ ํ˜ธ์ถœ๋  ๋•Œ ๋ชจ๋“  ์ฝœ๋ฐฑ์ด ์—ญ์ฐธ์กฐ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์—ญ์ฐธ์กฐ๋˜๊ณ  ํ”„๋ผ๋ฏธ์Šค๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด no-op์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์•ฝ์†์ด ๋งˆ์šดํŠธ๋˜์ง€ ์•Š์€ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๊ฒƒ๊ณผ ๊ด€๋ จ๋œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ProtectFromUnmount ์†Œ์Šค

์ด ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์€ ๋ชจ๋“  ์•ฝ์†์— ์ทจ์†Œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then((val) =>
      hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    );
    promise.catch((error) =>
      hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

ํŽธ์ง‘: ์ •ํ™•์„ฑ/์™„์ „์„ฑ์„ ์œ„ํ•ด ์—…๋ฐ์ดํŠธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

const somePromise = new Promise(r => setTimeout(r, 1000));

const cancelable = makeCancelable(somePromise);

cancelable
  .promise
  .then(() => console.log('resolved'))
  .catch(({isCanceled, ...error}) => console.log('isCanceled', isCanceled));

// Cancel promise
cancelable.cancel();

ES6 ์•ฝ์†์„ ์ทจ์†Œํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‚˜์—ดํ•˜๋Š” ๊ฒƒ์€ ์š”์ ์ด ์•„๋‹™๋‹ˆ๋‹ค. ๊ทธ ์˜๋„๋Š” ์‚ฌ์–‘ ์ฃผ๋ณ€์—์„œ ์ž‘๋™ํ•˜๋ ค๊ณ  ํ•˜๊ธฐ ๋ณด๋‹ค๋Š” ์‚ฌ์–‘๊ณผ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋Š” ์†”๋ฃจ์…˜์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋™์˜ํ•œ๋‹ค. ํ”„๋ผ๋ฏธ์Šค ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์•˜์„ ๋•Œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—ฌ์ „ํžˆ ๋งˆ์šดํŠธ๋˜์–ด ์žˆ๋Š”์ง€ ๋‹จ์ˆœํžˆ ํ™•์ธํ•˜๋Š” ๋Œ€์‹  ๋ชจ๋“  ์ข…๋ฅ˜์˜ ๋งˆ๋ฒ•์— ์˜์กดํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ ํ”„๋ผ๋ฏธ์Šค๋ฅผ "์–ธ๋ฐ”์ธ๋“œ"ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
๋‚˜์—๊ฒŒ ๊ทธ๊ฒƒ์€ ๊ฐ„๋‹จํ•œ ํ…Œ์ŠคํŠธ๊ฐ€ ์ด๊ฒƒ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์ธ ์†”๋ฃจ์…˜์„ ๊ณผ๋„ํ•˜๊ฒŒ ์—”์ง€๋‹ˆ์–ด๋งํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋Š๊ปด์ง‘๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ๊ฐ„๋‹จํ•œ ๊ฒ€์‚ฌ๋ฅผ ๊ณ„์†ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

React.createClass(function() {
  componentDidMount: function() {
    this._isMounted = true;

    ajax(/* */).then(this.handleResponse);
  }

  handleResponse: function(response) {
    if (!this._isMounted) return; // Protection

    /* */
  }

  componentWillUnmount: function() {
    this._isMounted = false;
  }
});

์ด๊ฒƒ์€ ๋ฌผ๋ก  ๋‚ด ์˜๊ฒฌ์ด์ง€๋งŒ ๋ฐ˜์‘ ๊ตฌ์„ฑ ์š”์†Œ ๋‚ด๋ถ€์— ์•ฝ์†์ด ์žˆ๋Š” ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ ๋กœ๋“œ๋Š” ์šฐ๋ฆฌ ๊ณ ์œ ์˜ ์ƒ์šฉ๊ตฌ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋Œ€์‹  ๋ฐ˜์‘์œผ๋กœ ๋‹ค๋ฃจ์–ด์•ผ ํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ์‹œ๋‚˜๋ฆฌ์˜ค์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ๋Š” ์‹ค์ œ ๋งˆ์šดํŠธ ์ƒํƒœ๋ฅผ ๋”ฐ๋ฅด๊ธฐ ์œ„ํ•ด ๋ฐ˜์‘์ด ๊ฐ ๊ตฌ์„ฑ ์š”์†Œ(์ •์˜๋œ ๊ฒฝ์šฐ componentDidMount๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผ)์—์„œ DOM ๋งˆ์šดํŠธ ํ”„๋กœ์„ธ์Šค๋ฅผ ์™„๋ฃŒํ•  ๋•Œ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•˜์ง€๋งŒ ํ•„์š”ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์ €๊ธฐ ๋†”๋‘๋Š” ๊ฒƒ. componentDidMount๊ฐ€ ์ •์˜๋˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ๊ตฌ์„ฑ ์š”์†Œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ DOM ๋งˆ์šดํŠธ ์ค€๋น„๋ฅผ ์ˆ˜์‹ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

setState ๊ฐ€ ์›ํ•˜๋Š” ์ƒํƒœ ๋ณ€๊ฒฝ์œผ๋กœ ํ•ด๊ฒฐ๋˜๋Š” ์—ฐ๊ฒฐ๋œ ์•ฝ์†์„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋งˆ์šดํŠธ ํ•ด์ œ๋˜๋ฉด ๋ณด๋ฅ˜ ์ค‘์ธ ์•ฝ์†์ด ์žˆ์œผ๋ฉด ์ตœ์ข… ๊ฒฐ๊ณผ๊ฐ€ ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค.

@istarkov ๋ฉ‹์ง„ ํŒจํ„ด, ์ข‹์•„์š”! ๋‹ค์Œ์€ ์•ฝ๊ฐ„ ๋ณ€๊ฒฝ๋œ API์ž…๋‹ˆ๋‹ค.

// create a new promise
const [response, cancel] = await cancelable(fetch('/api/data'));

// cancel it
cancel();

๋‚˜๋Š” React์— ์ต์ˆ™ํ•˜์ง€ ์•Š๊ณ  ๋ฌธ์„œ๋ฅผ ์ฝ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์„ ๋˜์ง€๊ธฐ ์œ„ํ•ด : Load Initial Data via Ajax ํŒ์€ .isMounted() ๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์›น ์‚ฌ์ดํŠธ๊ฐ€ ์›น ์‚ฌ์ดํŠธ์™€ ๋™์˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์œ„์˜ @istarkov ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์—ฌ componentWillUnmount ์—์„œ ์ดˆ๊ธฐ ๋กœ๋“œ๋ฅผ ์ทจ์†Œํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์™„์ „ํ•œ ํŒ์„ ๋ณด๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

@dtertman https://github.com/facebook/react/pull/5870 ์—์„œ ์ˆ˜์ •๋˜์—ˆ์œผ๋ฉฐ ๋ฌธ์„œ๊ฐ€ ์„ ํƒ๋˜๋ฉด ์˜จ๋ผ์ธ ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

@jimfb ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๊ฒ€์ƒ‰์—์„œ ์–ด๋–ป๊ฒŒ ๋†“์ณค๋Š”์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

@istarkov ๋Š” ์ด๊ฒƒ์ด ์˜๋„์ ์ธ ๊ฒƒ์ธ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์ง€๋งŒ ์›๋ž˜ ์•ฝ์†์ด ์‹คํŒจํ•˜๋ฉด makeCancelable ๊ฐ€ ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์›๋ž˜ ์•ฝ์†์ด ๊ฑฐ๋ถ€๋˜๋ฉด ์ฒ˜๋ฆฌ๊ธฐ๊ฐ€ ํ˜ธ์ถœ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์›๋ž˜ ์•ฝ์†์— ๋Œ€ํ•œ ์˜ค๋ฅ˜๋ฅผ ๊ณ„์† ์ฒ˜๋ฆฌํ•˜๊ธฐ๋ฅผ ์›ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์€ ์ด์ƒ์ ์ด์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ์›๋ž˜ ์•ฝ์†์—์„œ ๊ฑฐ๋ถ€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” makeCancelable ๋Œ€ํ•œ ์ œ ์ œ์•ˆ์ž…๋‹ˆ๋‹ค.

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then((val) =>
      hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    );
    promise.catch((error) =>
      hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

์ทจ์†Œ ๊ฐ€๋Šฅํ•œ ์•ฝ์†์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์ข‹์€ ์ƒ๊ฐ์ธ์ง€ ํ™•์‹ ํ•  ์ˆ˜ ์—†์ง€๋งŒ ์•ฝ์†์„ ์ทจ์†Œํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค๋ ค๋ฉด ๊ธฐ๋ณธ ๋™์ž‘์„ ์œ ์ง€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. :)

@vpontis : +1:

@istarkov ๊ท€ํ•˜์˜ ์›๋ž˜ ๊ฒŒ์‹œ๋ฌผ์€ ์—ฌ๊ธฐ์—์„œ ์ฐธ์กฐ๋ฉ๋‹ˆ๋‹ค: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html

๊ฒŒ์‹œ๋ฌผ์„ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๊นŒ ์•„๋‹ˆ๋ฉด ๊ฒŒ์‹œ๋ฌผ ์ž‘์„ฑ์ž์—๊ฒŒ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด์•ผ ํ•ฉ๋‹ˆ๊นŒ?

@vpontis ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค, ์ˆ˜์ •ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค! (https://github.com/facebook/react/pull/6152)

์•ˆ๋…•ํ•˜์„ธ์š” @jimfb , ์ธํ„ฐ๋„ท์—์„œ ๋‹น์‹ ์„

makeCancelable ํ•จ์ˆ˜์˜ ๋˜ ๋‹ค๋ฅธ ๋ฒ„๊ทธ ์ˆ˜์ •: ์ตœ๊ทผ ๋…ธ๋“œ ๋ฒ„์ „์—์„œ UnhandledPromiseRejectionWarning ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(ํŠนํžˆ ์ƒˆ ๋ฒ„์ „์˜ ๋…ธ๋“œ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ). ๋…ธ๋“œ 6.6.0 ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ ์ค‘ ํ•˜๋‚˜๋Š” ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์€ ๋ชจ๋“  ์•ฝ์†์ด ๊ฑฐ๋ถ€๋˜๋ฉด ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. @vpontis ์˜ ๊ธฐ์กด ์ฝ”๋“œ์—๋Š” ๋™์ผํ•œ ๊ธฐ๋ณธ ์•ฝ์†์— ๋Œ€ํ•ด ๋ณ„๋„์˜ then ๋ฐ catch ํ˜ธ์ถœ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํšจ๊ณผ์ ์œผ๋กœ, ์ด๊ฒƒ์€ ์„ฑ๊ณต๋งŒ ์ฒ˜๋ฆฌํ•˜๋Š” ์•ฝ์†๊ณผ ์˜ค๋ฅ˜๋งŒ ์ฒ˜๋ฆฌํ•˜๋Š” ์•ฝ์†์„ _2_ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์ฒซ ๋ฒˆ์งธ ์•ฝ์†์ด ๋…ธ๋“œ์—์„œ ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์€ ์•ฝ์† ๊ฑฐ๋ถ€๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค.

์ˆ˜์ •์€ ๋งค์šฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. ์„ฑ๊ณต ๋ฐ ์˜ค๋ฅ˜ ํ•ธ๋“ค๋Ÿฌ ๋ชจ๋‘์— ๋Œ€ํ•ด ํ•˜๋‚˜์˜ ์•ฝ์†์„ ํ•˜๋„๋ก ๋‘ ํ˜ธ์ถœ์„ ์—ฐ๊ฒฐํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ๊ณ ์ • ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise
      .then((val) =>
        hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
      )
      .catch((error) =>
        hasCanceled_ ? reject({isCanceled: true}) : reject(error)
      );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

@alangpierce ์ •๋‹ต ์— ๋งค์šฐ ๊ฐ€๊น์ง€๋งŒ ์ •ํ™•ํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. resolve() ๋˜๋Š” reject() ๊ฐ€ ํ•ด๊ฒฐ๋œ ํ”„๋ผ๋ฏธ์Šค์— ๋Œ€ํ•ด ์–ด๋–ค ์ด์œ ๋กœ๋“  ๋™๊ธฐ์ ์œผ๋กœ throwํ•˜๋ฉด ๋‘ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋ชจ๋‘ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ์ฑ…์€ .then(onFulfilled, onRejected) ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then(
      (val) => hasCanceled_ ? reject({isCanceled: true}) : resolve(val),
      (error) => hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

์ด makeCancelable ์†”๋ฃจ์…˜์€ isMounted()๊ฐ€ ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ์ด์œ ์— ๋Œ€ํ•ด ํฌ์ธํŠธ 3์„ ๋ณผ ๋•Œ isMounted() ํ˜ธ์ถœ๊ณผ ํšจ๊ณผ์ ์œผ๋กœ ๋™์ผํ•˜์ง€ ์•Š์Šต๋‹ˆ๊นŒ?

๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์™„์ „ํžˆ ๋งˆ์šดํŠธ ํ•ด์ œ๋˜๋ฉด setState๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
์ด๊ฒƒ์€ ๋น„๋™๊ธฐ ์ฝœ๋ฐฑ์ด ์ œ๋Œ€๋กœ ์ •๋ฆฌ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฐ•๋ ฅํ•œ ํ‘œ์‹œ์ž…๋‹ˆ๋‹ค. ๋ถˆํ–‰ํžˆ๋„, ์ฃผ๋ฅ˜ JS API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋งค๋‹ฌ๋ฆฐ ๋น„๋™๊ธฐ ์ฝœ๋ฐฑ์„ ์ •๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ๋งค์šฐ ์‰ฝ๊ฒŒ ํ”ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜๋‚˜์˜ ์ฝœ๋ฐฑ์€ ํฐ ๋ฌธ์ œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํ•ด๋‹น ์ฝœ๋ฐฑ์€ ๊ฐ์ฒด ๋ฐ ์ค‘๊ฐ„ ์ฝœ๋ฐฑ, ์•ฝ์† ๋ฐ ๊ตฌ๋…์— ๊ณ„์† ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค. ๋งŽ์€ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋ฌธ์ œ๊ฐ€ ๋น ๋ฅด๊ฒŒ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

makeCancellable์€ ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๋ณด์œ ํ•˜๋Š” ํ•จ์ˆ˜์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๋ณด์œ ํ•˜๊ฒŒ ๋˜๋Š” ๋˜ ๋‹ค๋ฅธ ์•ฝ์†์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. makeCancellable ์†”๋ฃจ์…˜์€ ๋ถ€์šธ ์†์„ฑ isMounted๋ฅผ ์•ฝ์†์œผ๋กœ ์˜ฎ๊ธฐ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

GC ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด cancel()์ด ํ˜ธ์ถœ๋  ๋•Œ null์„ ์ œ๊ฑฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์—ฌ์ „ํžˆ ๋น„๋™๊ธฐ ํ”„๋กœ์„ธ์Šค์—์„œ ๊ตฌ์„ฑ ์š”์†Œ๋กœ์˜ ์ฐธ์กฐ ์ฒด์ธ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

class CancellableDeferred {
  constructor(request) {
    this.deferred = $.Deferred();

    request.then((data) => {
      if (this.deferred != null) {
        this.deferred.resolve(data);
      }
    });

    request.fail((data) => {
      if (this.deferred != null) {
        this.deferred.reject(data);
      }
    });
  }

  cancel() {
    this.deferred = null;
  } 

  promise() {
    return this.deferred.promise();
  }
}

-> jQuery ์ง€์—ฐ ๊ฐ์ฒด๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. Promise API์— ๋Œ€ํ•ด ์ž˜ ์•Œ์ง€ ๋ชปํ•ด์„œ ์–ด๋–ป๊ฒŒ ๋ณด์ผ์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด๋Š” cancel()์ด ํ˜ธ์ถœ๋˜๊ณ  deferred๊ฐ€ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ deferred๋ฅผ ๊ฑฐ๋ถ€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ์‚ฌ๋žŒ๋“ค์€ ์ด๊ฒƒ์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•ด์•ผ ํ•˜๋Š”์ง€์— ๋Œ€ํ•ด ๋‹ค๋ฅธ ์˜๊ฒฌ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ฒด์ธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

AJAX ์š”์ฒญ -> ํ์‡„ -> CancellableDeferredInstance -> JQuery ์ง€์—ฐ -> ๊ตฌ์„ฑ ์š”์†Œ

๊ทธ๋Ÿฐ ๋‹ค์Œ ์ทจ์†Œ ํ›„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณด์ž…๋‹ˆ๋‹ค.

AJAX ์š”์ฒญ -> ํ์‡„ -> CancellableDeferredInstance /๊ฐ์ฒด ์ฐธ์กฐ๊ฐ€ ์ด์ œ null/ JQuery ์ง€์—ฐ -> ๊ตฌ์„ฑ ์š”์†Œ

๋”ฐ๋ผ์„œ AJAX ์š”์ฒญ์€ ๋” ์ด์ƒ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ GCd๊ฐ€ ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์•ผ ํ์‡„....]

@benmmurphy ์•ˆ๋…•ํ•˜์„ธ์š”. ์ €๋Š” JS ๊ฐ€๋น„์ง€ ์ปดํŒŒ์ผ์— ์ต์ˆ™ํ•˜์ง€ ์•Š๊ณ  React์—์„œ๋Š” ๋‹ค๋ฅด๊ฒŒ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ดํ•ด๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

makeCancellable ์‚ฌ์šฉํ•˜๋ฉด React ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋งˆ์šดํŠธ ํ•ด์ œ๋  ๋•Œ ๊ฐ€๋น„์ง€ ์ˆ˜์ง‘์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

makeCancellable์€ ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๋ณด์œ ํ•˜๋Š” ํ•จ์ˆ˜์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๋ณด์œ ํ•˜๊ฒŒ ๋˜๋Š” ๋˜ ๋‹ค๋ฅธ ์•ฝ์†์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. makeCancellable ์†”๋ฃจ์…˜์€ ๋ถ€์šธ ์†์„ฑ isMounted๋ฅผ ์•ฝ์†์œผ๋กœ ์˜ฎ๊ธฐ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

makeCancellable :

handleError() {
  if (this.isMounted()) {
    console.log('ERROR')
  }
}

makeCancellable :

promise.then(...).fail((reason) => {
  if (reason.isCancelled) return;
  console.log('ERROR');
})

makeCancellable ์—†์œผ๋ฉด ์—ฌ์ „ํžˆ this ๋Œ€ํ•œ ์ฐธ์กฐ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋งˆ์šดํŠธ ํ•ด์ œ๋  ๋•Œ ๊ฐ€๋น„์ง€ ์ˆ˜์ง‘๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‹ค๋ฅธ ๊ฒฝ์šฐ์—๋Š” ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋งˆ์šดํŠธ ํ•ด์ œ๋˜์ž๋งˆ์ž ์ทจ์†Œ ๊ฐ€๋Šฅํ•œ ํ”„๋ผ๋ฏธ์Šค์˜ ์‹คํŒจ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฏ€๋กœ ๋” ์ด์ƒ ์ฐธ์กฐ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@vpontis

๋ฌธ์ œ๋ฅผ ์„ค๋ช…ํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ nodejs ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. Foo ๊ตฌ์„ฑ ์š”์†Œ๋Š” ๋น„๋™๊ธฐ ์ฝœ๋ฐฑ resolve ์ด null๋กœ ์„ค์ •๋œ ๊ฒฝ์šฐ์—๋งŒ GC๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ 30์ดˆ๊ฐ€ ๊ฑธ๋ฆฌ๋Š” Ajax ์š”์ฒญ์„ ์‹คํ–‰ํ•˜๋ฉด ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋งˆ์šดํŠธ ํ•ด์ œ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๊ตฌ์„ฑ ์š”์†Œ๋Š” 30์ดˆ ๋™์•ˆ GCd๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ isMount()๋ฅผ ๋” ์ด์ƒ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ์œผ๋กœ์จ ํ•ด๊ฒฐํ•˜๋ ค๋Š” ๋ฌธ์ œ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

npm install promise
npm install weak

node --expose-gc gc.js
first gc Foo {}
after first gc Foo {}
after resolve = null Foo {}
foo gc'd
after second gc {}

https://gist.github.com/benmmurphy/aaf35a44a6e8a1fbae1764ebed9917b6

ํŽธ์ง‘ํ•˜๋‹ค:

๊ณผ๊ฑฐ ์ด์•ผ๊ธฐ๋ฅผ ํ•ด์„œ ๋ฏธ์•ˆํ•˜์ง€๋งŒ ์ฒ˜์Œ ๊ฒŒ์‹œ๋ฌผ์„ ์ฝ์—ˆ์„ ๋•Œ๋Š” ๋‹น์‹ ์ด ๋งํ•˜๋ ค๋Š” ์š”์ ์„ ์ดํ•ดํ•˜์ง€ ๋ชปํ–ˆ๋Š”๋ฐ ์ง€๊ธˆ์€ ์•Œ ๊ฒƒ ๊ฐ™์•„์š”. ๋‹น์‹ ์ด ๋งํ•˜๋ ค๋Š” ๊ฒƒ์€ ์˜ค๋ฅ˜ ์ฝœ๋ฐฑ์ด ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—(๋˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๋”ฐ๋ฅด์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—) ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์•ฝ์†์— ์˜ํ•ด ์ฐธ์กฐ๋˜๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์‚ฌ์‹ค์ž…๋‹ˆ๋‹ค. ๊ธ€์Ž„, ์ฒซ ๋ฒˆ์งธ ๋ถ€๋ถ„์€ ์‚ฌ์‹ค์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๋Ÿฌํ•œ ์ถ”๋ก ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

1) ์˜ˆ์ œ์˜ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๊ธฐ์— ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ ์ฐธ์กฐ๊ฐ€ ์—†๋”๋ผ๋„ then() ์ฝœ๋ฐฑ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด then ํ•ธ๋“ค์€ ์ผ๋ฐ˜์ ์œผ๋กœ this.setState(...) ํ•ฉ๋‹ˆ๋‹ค.
2) ์˜ˆ์ œ์˜ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๊ธฐ์— ๋Œ€๋ถ€๋ถ„์˜ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๊ธฐ๊ฐ€ ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ ์ฐธ์กฐ๊ฐ€ ์—†๋”๋ผ๋„. ์˜ˆ๋ฅผ ๋“ค์–ด ๊ทธ๋“ค์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค:

promise.then(...).fail((reason) => {
  if (reason.isCancelled) return;
  console.log('ERROR');

  this.setState({error: true});
})

3) ์ฝ”๋“œ๊ฐ€ then() ์ฝœ๋ฐฑ์„ ๋”ฐ๋ฅด์ง€ ์•Š๊ณ  isCancelled ๋ณ€์ˆ˜๋ฅผ ํ™•์ธํ•œ ํ›„ ํ•จ์ˆ˜๋ฅผ ์ข…๋ฃŒํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์ง€๋งŒ GC๋Š” ์ด๋ฅผ ์•Œ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ๋‚ด ์˜ˆ์ œ๋‚˜ ์ด์— ๊ธฐ๋ฐ˜ํ•œ ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— ์‹ค์ œ๋กœ GC๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ˆ˜ํ–‰๋˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์•„์ง ๋‚ด ๊ฒƒ์„ ํ…Œ์ŠคํŠธํ•˜์ง€ ์•Š์•˜์œผ๋ฉฐ ์–ด๋ฆฌ์„์€ ์˜ค๋ฅ˜๋ฅผ ๋ฒ”ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ž‘๋™ํ•˜์ง€ ์•Š์œผ๋ฉด ๋†€๋ผ์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค./

์•ฝ์† API ์ธก๋ฉด์—์„œ ์ด๊ฒƒ์€ GC ์ธก๋ฉด์—์„œ nodejs์—์„œ ์ €์—๊ฒŒ ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ _resolve , _reject ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํด๋กœ์ € ๊ทผ์ฒ˜์— ๋‘์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด JS ์‚ฌ์–‘์— ๋”ฐ๋ผ ์ž‘๋™ํ•˜๋Š”์ง€ ๋˜๋Š” ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋…ธ๋“œ๊ฐ€ ์ผ๋ถ€ ์ตœ์ ํ™”๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๊ตฌํ˜„์ด ๋ณด์ด๋Š” ๋ชจ๋“  ๋ณ€์ˆ˜๋ฅผ ์บก์ฒ˜ํ•˜๊ฑฐ๋‚˜ ํด๋กœ์ €์—์„œ ์ฐธ์กฐ๋˜๋Š” ๋ณ€์ˆ˜๋งŒ ์บก์ฒ˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? JS๋ฅผ ์‹ค์ œ๋กœ ์ดํ•ดํ•˜๋Š” ์‚ฌ๋žŒ์ด ์ฐจ์ž„๋ฒจ์„ ๋“ค๊ณ  ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์„์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. :)

var makeCancelable = (promise) => {
  let resolve;
  let reject;

  const wrappedPromise = new Promise((_resolve, _reject) => {
    resolve = _resolve;
    reject = _reject;

    promise.then((val) => {
       if (resolve != null) resolve(val)
    });
    promise.catch((error) => {
       if (reject != null) reject(error)
    });
  });

  return {
    promise: wrappedPromise,
    cancel() {
      resolve = null;
      reject = null;
    },
  };
};

isMounted ํ•จ์ˆ˜๊ฐ€ 16.0์—์„œ ์ œ๊ฑฐ๋ฉ๋‹ˆ๊นŒ?

@istarkov ์ฝ”๋“œ๋กœ ์•ฝ๊ฐ„์˜ ๊ฐœ์„ ์„ ์œ„ํ•œ ์ œ์•ˆ:

const makeCancelable = (promise) => {
    let hasCanceled_ = false
    promise.then((val) =>
        hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    )
    .catch((error) =>
        hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    )

    return {
        promise,
        cancel() {
            hasCanceled_ = true
        }
    }
}

์ƒˆ๋กœ์šด ์•ฝ์†์ด ๋ถˆํ•„์š”ํ•  ๋ฟ์ž…๋‹ˆ๋‹ค.

์ƒˆ๋กœ์šด ์•ฝ์†์ด ๋ถˆํ•„์š”ํ•  ๋ฟ์ž…๋‹ˆ๋‹ค.

@BnayaZil resolve ๋ฐ reject ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์žˆ์ง€๋งŒ ์–ด๋””์—์„œ ์™”๋Š”์ง€ ๋ช…ํ™•ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Promise.resolve ๋ฐ Promise.reject ๋ฅผ ์˜๋ฏธํ–ˆ์Šต๋‹ˆ๊นŒ? ์ด ๊ฒฝ์šฐ์—๋„ ์—ฌ์ „ํžˆ ์ƒˆ๋กœ์šด Promise๋ฅผ ๋ฐ˜ํ™˜ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ฉฐ์น  ์ „์— fetch() ์š”์ฒญ์„ ์ค‘๋‹จํ•  ์ˆ˜ ์žˆ๋Š” ์ƒˆ API๊ฐ€ DOM ์‚ฌ์–‘์— ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด API๋Š” ์•„์ง ์–ด๋–ค ๋ธŒ๋ผ์šฐ์ €์—์„œ๋„ ๊ตฌํ˜„๋˜์ง€ ์•Š์•˜์ง€๋งŒ "abortcontroller-polyfill"์ธ NPM์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํด๋ฆฌํ•„์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ํด๋ฆฌํ•„์€ ๋ณธ์งˆ์ ์œผ๋กœ @istarkov๊ฐ€ ๊ฒŒ์‹œํ•œ ์ฝ”๋“œ์™€ ๋™์ผํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ ํ•˜์ง€๋งŒ ๊ตฌํ˜„๋˜๋ฉด ์ฝ”๋“œ ๋ณ€๊ฒฝ ์—†์ด ์‹ค์ œ ๋ธŒ๋ผ์šฐ์ € API๋กœ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์„ธ๋ถ€ ์ •๋ณด:
https://mo.github.io/2017/07/24/abort-fetch-abortcontroller-polyfill.html

React.createClass ๋Š” ๋” ์ด์ƒ React 16์— ์กด์žฌํ•˜์ง€ ์•Š๊ณ  ์ƒˆ๋กœ์šด create-react-class ํŒจํ‚ค์ง€์—๋Š” isMounted ๋Œ€ํ•œ ๋ช…ํ™•ํ•œ ์‚ฌ์šฉ ์ค‘๋‹จ ๋ฉ”์‹œ์ง€๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์ด๋ฅผ ์ข…๋ฃŒํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” @istarkov์˜ ์†”๋ฃจ์…˜์„ ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•˜๋‹ค๋Š” ๊ฒƒ์„ @benmmurphy์— ๋™์˜ isMounted() ๊ฐ€ ์“ฐ๋ ˆ๊ธฐ ์ˆ˜์ง‘ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—.

@benmmurphy ์˜ ์†”๋ฃจ์…˜์€ ๋” ๊ฐ€๊น์ง€๋งŒ ์ž˜๋ชป๋œ ๋ณ€์ˆ˜๋ฅผ

ํ•ต์‹ฌ์€ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์—ญ์ฐธ์กฐํ•˜๋Š” ํด๋กœ์ €๋ฅผ ํ†ตํ•ด ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

const makeCancelable = promise => {
  let cancel = () => {};

  const wrappedPromise = new Promise((resolve, reject) => {
    cancel = () => {
      resolve = null;
      reject = null;
    };

    promise.then(
      val => {
        if (resolve) resolve(val);
      },
      error => {
        if (reject) reject(error);
      }
    );
  });

  wrappedPromise.cancel = cancel;
  return wrappedPromise;
};

์ด ์†”๋ฃจ์…˜์ด ์ด์ „ ์†”๋ฃจ์…˜์ด ์•„๋‹Œ ๊ฐ€๋น„์ง€ ์ˆ˜์ง‘์„ ํ—ˆ์šฉํ•˜๋Š” ์ด์œ ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์„ค๋ช…์€ ์—ฌ๊ธฐ ์—์„œ ์ฐพ์„ ์ˆ˜

๋‚˜๋Š” ์ด๊ฒƒ์„ npm ํŒจํ‚ค์ง€๋กœ ์“ฐ๋ ˆ๊ธฐํ†ต์œผ๋กœ ๋ฐ”๊ฟจ์Šต๋‹ˆ๋‹ค . ์œ ์Šค ์ผ€์ด์Šค IS ๋ฐ˜์‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋‚˜๋Š”์ด ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋งˆ์šดํŠธ ํ•ด์ œ ์–ป์„ ๋•Œ ํŠธ๋ž™์˜ ์•ฝ์†๊ณผ์ด๋ฅผ ์ทจ์†Œํ•˜๋Š” HOC ์„ฑ๋ถ„์œผ๋กœ, trashable-๋ฐ˜์‘ .

ํŽธ์ง‘ : ๋‚ด ๋‚˜์œ, ๋ฐฉ๊ธˆ @hjylewis thrashable ์„ ๋ณด์•˜๊ณ  ์•ฝ์†๋„ ์ทจ์†Œํ•ฉ๋‹ˆ๋‹ค. ์—ฌ์ „ํžˆ ์•„๋ž˜ ํŒจํ„ด์€ IMO๊ฐ€ ์•ฝ๊ฐ„ ๊ฐœ์„ ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ์†”๋ฃจ์…˜ ์ค‘ ์–ด๋Š ๊ฒƒ๋„ ์•ฝ์†์„ ์ทจ์†Œํ•˜์ง€ ์•Š์œผ๋ฉฐ ์˜์›ํžˆ ๋ณด๋ฅ˜ ์ค‘์ธ ์•ฝ์†์„ ํก์ˆ˜ํ•˜์—ฌ ์—ฐ์žฅ ์—†์ด ์ทจ์†Œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function makeCancelable(promise) {
  let active = true;
  return {
    cancel() {active = false},
    promise: promise.then(
      value => active ? value : new Promise(()=>{}),
      reason => active ? reason : new Promise(()=>{})
    )
  }
}

// used as above:

const {promise, cancel} = makeCancelable(Promise.resolve("Hey!"))

promise.then((v) => console.log(v)) // never logs
cancel()

์—ฌ๊ธฐ์—์„œ ์‚ด๋‹ค

GC์™€ ๊ด€๋ จํ•˜์—ฌ ์ˆ˜์ •ํ•ด์•ผ ํ•  ๋ฏธ๋ฌ˜ํ•จ์ด ์žˆ์„ ์ˆ˜ ์žˆ๊ณ  ์ปคํ”ผ๊ฐ€ ์•„์ง ์‹œ์ž‘๋˜์ง€ ์•Š์•˜์ง€๋งŒ ๊ทธ ํŒจํ„ด์€ ๋ฐ˜ํ™˜๋œ ์•ฝ์†์ด ์‹ค์ œ๋กœ ์ทจ์†Œ๋˜๊ณ  ๋ˆ„์ถœ๋˜์ง€ ์•Š๋„๋ก ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(๊ณผ๊ฑฐ์— ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค).

@pygy ๋‹ต๋ณ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

๋ถˆํ–‰ํžˆ๋„ ๊ท€ํ•˜์˜ ์†”๋ฃจ์…˜์€ ์—ฌ์ „ํžˆ โ€‹โ€‹๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ณธ์งˆ์ ์œผ๋กœ ์กฐ๊ฑด๋ถ€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” @istarkov ์˜ ์†”๋ฃจ์…˜์„ ๋‹ค์‹œ ์ž‘์„ฑ

์ด ๊ตฌํ˜„์„ ํœด์ง€ํ†ต์— ๋†“๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ์‰ฝ๊ฒŒ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(๊ฐ€๋น„์ง€ ์ˆ˜์ง‘ ํ…Œ์ŠคํŠธ ์‹คํŒจ).

๋˜ํ•œ ๊ตฌํ˜„์—์„œ ์˜ค๋ฅ˜๋ฅผ ์ œ๋Œ€๋กœ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.

2018๋…„์ž…๋‹ˆ๋‹ค. ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ๊ฒƒ๋ณด๋‹ค ๋” ๋‚˜์€ ์ ‘๊ทผ ๋ฐฉ์‹์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์˜ˆ, ๋ฐ˜์‘ ๋„ค์ดํ‹ฐ๋ธŒ์˜ ๋‘ ๋ฐฐ ํฌ๊ธฐ์˜ ๋ฌธ์„œ๊ฐ€ ์žˆ์ง€๋งŒ ๋งค์šฐ ์ „๋ฌธ์ ์ธ ์ผ๋ถ€ ํƒ์ƒ‰ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•ฝ์† "์ทจ์†Œ"์— ๋Œ€ํ•œ ์ด๋Ÿฌํ•œ ์Šค๋‹ˆํŽซ์€ ๊ทธ๋‹ค์ง€ ์ข‹์€ IMHO๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์ทจ์†Œ๋œ ์•ฝ์†์€ ์›๋ž˜ ์•ฝ์†์ด ํ•ด๊ฒฐ๋  ๋•Œ๊นŒ์ง€ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ isMounted ํŠธ๋ฆญ์„ ์‚ฌ์šฉํ•  ๋•Œ๊นŒ์ง€ ๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ ์ ˆํ•œ ์ทจ์†Œ ๊ฐ€๋Šฅํ•œ ํ”„๋ผ๋ฏธ์Šค ๋ž˜ํผ๋Š” ๋‘ ๋ฒˆ์งธ ํ”„๋ผ๋ฏธ์Šค์™€ Promise.race๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰ Promise.race([originalPromise, cancelationPromise])

@benmmurphy ์˜ ์†”๋ฃจ์…˜์€ ๋” ๊ฐ€๊น์ง€๋งŒ ์ž˜๋ชป๋œ ๋ณ€์ˆ˜๋ฅผ

๋‚ด ์†”๋ฃจ์…˜์ด ์ž‘๋™ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ ์ž๋ฐ” ์Šคํฌ๋ฆฝํŠธ ๋Ÿฐํƒ€์ž„์ด ํ™•์‹คํžˆ ์•Œ๊ธฐ ์œ„ํ•ด ์ œ๊ณตํ•˜๋Š” ์•ฝ์†์— ๋Œ€ํ•ด ์ถฉ๋ถ„ํžˆ ์•Œ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ํ•˜๋„ค์Šค์˜ ๋…ธ๋“œ์—์„œ ์†”๋ฃจ์…˜์„ ์‹คํ–‰ํ•˜๋ฉด ๊ฐ’์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ GCํ•ฉ๋‹ˆ๋‹ค. ๋‚ด ์†”๋ฃจ์…˜์€ ํ•ด๊ฒฐ/๊ฑฐ๋ถ€ ๊ธฐ๋Šฅ์„ ๋” ๋†’์€ ๋ฒ”์œ„์— ํ• ๋‹นํ•œ ๋‹ค์Œ ์ทจ์†Œ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ์ด๋Ÿฌํ•œ ๊ฐ’์„ ๋ฌดํšจํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํ•จ์ˆ˜๋Š” ํ•˜์œ„ ๋ฒ”์œ„์—์„œ ๊ณ„์† ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ์ง€๋งŒ ์ฐธ์กฐ๋˜์ง€๋Š” ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ˜„๋Œ€ ์ž๋ฐ” ์Šคํฌ๋ฆฝํŠธ ์—”์ง„์€ ์ฐธ์กฐ๋˜์ง€ ์•Š๋Š” ํ•œ ํด๋กœ์ €์—์„œ ๋ณ€์ˆ˜๋ฅผ ์บก์ฒ˜ํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๊ฒƒ์ด ์‚ฌ๋žŒ๋“ค์ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ผ์„ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ˆ˜๋กœ DOM ๋ˆ„์ˆ˜๋ฅผ ๋งŒ๋“œ๋Š” ํฐ ๋ฌธ์ œ์˜€๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. var element = findDOM(); element.addEventListener('ํด๋ฆญ', function() {}); ๋ฐ ์š”์†Œ๋Š” ํด๋กœ์ €์—์„œ ์‚ฌ์šฉ๋˜์ง€ ์•Š์•˜๋”๋ผ๋„ ํด๋กœ์ €์—์„œ ์ฐธ์กฐ๋ฉ๋‹ˆ๋‹ค.

@hjylewis @benmmurphy ์™œ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์—ญ์ฐธ์กฐํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ ?? ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์‹คํ–‰๋œ ํ›„์—๋Š” ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋งž๋‚˜์š”??

์•ฝ์† "์ทจ์†Œ"์— ๋Œ€ํ•œ ์ด๋Ÿฌํ•œ ์Šค๋‹ˆํŽซ์€ ๊ทธ๋‹ค์ง€ ์ข‹์€ IMHO๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์ทจ์†Œ๋œ ์•ฝ์†์€ ์›๋ž˜ ์•ฝ์†์ด ํ•ด๊ฒฐ๋  ๋•Œ๊นŒ์ง€ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ isMounted ํŠธ๋ฆญ์„ ์‚ฌ์šฉํ•  ๋•Œ๊นŒ์ง€ ๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ ์ ˆํ•œ ์ทจ์†Œ ๊ฐ€๋Šฅํ•œ ํ”„๋ผ๋ฏธ์Šค ๋ž˜ํผ๋Š” ๋‘ ๋ฒˆ์งธ ํ”„๋ผ๋ฏธ์Šค์™€ Promise.race๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰ Promise.race([originalPromise, cancelationPromise])

@hjylewis ์™€ ๊ด‘์‚ฐ์€ ์‹ค์ œ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๊นŒ? ๋…ธ๋“œ๊ฐ€ ์•ฝํ•œ ์ƒํƒœ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๊ฒƒ๋“ค์„ ๋‹ค์‹œ ๋ณด๋ฉด ๋‚˜๋Š” ๊ทธ๋“ค ์ค‘ ์–ด๋Š ์ชฝ๋„ ํŠน์ดํ•œ ์ž‘์„ฑ๋œ ์•ฝ์† ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ๋Š” ๋ฐ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ์•ฝ์† ์‚ฌ์šฉ์ž๋กœ์„œ ๊ฑฐ๋ถ€๋œ ์ƒํƒœ์—์„œ ํ•ด๊ฒฐ๋  '์ทจ์†Œ๋œ' ์•ฝ์†์„ ์˜ˆ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋‘˜ ๋‹ค ์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ตฌ์„ฑ ์š”์†Œ์˜ ๊ฒฝ์šฐ ๊ฑฐ๋ถ€ ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ๋ฌด์‹œํ•˜๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ํ•„์š”๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉํ•˜๊ธฐ ๋” ์‰ฌ์šด ์†”๋ฃจ์…˜์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ํŠน์ดํ•œ ๊ฑฐ๋ถ€ ๊ฐ€๋Šฅํ•œ ์•ฝ์†์ด Promise.race([])๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ทจ์†Œ ๊ฐ€๋Šฅํ•œ ์•ฝ์†์„ ๋งŒ๋“ค ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. Promise๊ฐ€ ํ•ด๊ฒฐ๋˜๋ฉด ๋ณด๋ฅ˜ ์ค‘์ธ ์ฝœ๋ฐฑ์ด ์‚ญ์ œ๋˜์–ด ๊ทธ ์‹œ์ ์—์„œ ๋ธŒ๋ผ์šฐ์ € ๋„คํŠธ์›Œํฌ์—์„œ ๊ตฌ์„ฑ ์š”์†Œ๋กœ์˜ ์ฐธ์กฐ ์ฒด์ธ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด Race Promise์™€ ๊ตฌ์„ฑ ์š”์†Œ ์‚ฌ์ด์— ๋” ์ด์ƒ ์ฐธ์กฐ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ทจ์†Œ ๊ฐ€๋Šฅํ•œ ์•ฝ์†๊ณผ ํ•จ๊ป˜ Promise.all() ์‚ฌ์šฉํ•˜๊ณ  ๋ธŒ๋ผ์šฐ์ € ์ฝ˜์†”์—์„œ ์žกํžˆ์ง€ ์•Š๋Š” ์˜ค๋ฅ˜๋ฅผ ํ”ผํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค... ์ฒซ ๋ฒˆ์งธ ์ทจ์†Œ ์˜ค๋ฅ˜๋งŒ ์žก์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ๊ฒƒ๋“ค์€ ์žกํžˆ์ง€ ์•Š์€ ์ฑ„๋กœ ๋‚จ์•„ ์žˆ์Šต๋‹ˆ๋‹ค.

2018๋…„์ž…๋‹ˆ๋‹ค. ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ๊ฒƒ๋ณด๋‹ค ๋” ๋‚˜์€ ์ ‘๊ทผ ๋ฐฉ์‹์ด ์žˆ์Šต๋‹ˆ๊นŒ?

ํ”„๋ผ๋ฏธ์Šค ์‹คํ–‰์„ ์ทจ์†Œํ•˜๋Š” ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•, ์ฆ‰ setTimeout, API ํ˜ธ์ถœ ๋“ฑ. 2019๋…„์ž…๋‹ˆ๋‹ค ๐Ÿ˜ญ ๐Ÿ˜ž

TC39์—์„œ ์ง„ํ–‰ ์ค‘์ธ Promise ์ทจ์†Œ ์Šค๋ ˆ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. (์ œ ์ƒ๊ฐ์—๋Š”) ์—ฌ๊ธฐ์— ๊ด€๋ จ์ด ์žˆ์Šต๋‹ˆ๋‹ค(์•„๋งˆ๋„ .. ํ™•์‹คํ•˜์ง€ ์•Š์Œ).
https://github.com/tc39/proposal-cancellation/issues/24

ํ”„๋ผ๋ฏธ์Šค ์‹คํ–‰์„ ์ทจ์†Œํ•˜๋Š” ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•, ์ฆ‰ setTimeout, API ํ˜ธ์ถœ ๋“ฑ. 2019๋…„์ž…๋‹ˆ๋‹ค ๐Ÿ˜ญ ๐Ÿ˜ž

์šฐ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ์„ ์ฐพ๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ?

const promise = new Promise(r => setTimeout(r, 1000))
  .then(() => console.log('resolved'))
  .catch(()=> console.log('error'))
  .canceled(() => console.log('canceled'));

// Cancel promise
promise.cancel();
์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰