React: إيقاف "isMounted"

تم إنشاؤها على ١٤ نوفمبر ٢٠١٥  ·  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

لقد قرأت التعليقات. أنا فقط أجد القضايا مستعصية على الحل.

لماذا الوعود لا يمكن الوثوق بها ملغاة؟ أي مصادر / براهين / أمثلة؟

في يوم الاثنين ، 16 نوفمبر 2015 ، كتب Evan Jacobs [email protected] :

أنا لا أتفق مع هذا. وعود ES6 على وجه الخصوص لا يمكن الاعتماد عليها
تم الإلغاء على componentWillUnmount ، لذا أزل الطريقة الوحيدة للتحقق مما إذا كان
يتم تثبيت المكون قبل setState أو يقوم إجراء آخر بفتح ملف
وسيلة لكثير من من الصعب تتبع البق غير المتزامن.

nvartolomei انظر إلى مواصفات الوعد ES6.

هذا هدف طويل المدى ، وليس شيئًا يحدث على الفور. لكننا نريد تتبع التخطيط والمناقشات في مكان واحد وليس عبر التعليقات في كل قضية عند ظهور ذلك. نحن ندرك أن مشكلة الوعود حاليًا غير قابلة للإلغاء وهذا سبب رئيسي لم نفعل ذلك بالفعل.

yaycmyk لتبسيط مشكلة معقدة للغاية ... التعليقات تقول ... استخدام isMounted لتجنب setState للمكونات غير المركبة لا يحل بالفعل مشكلة setState كان تحذير setState كنتيجة لوعد هو نوع من النمط المضاد على أي حال ، لأنه يمكن أن يتسبب في ظروف سباق لن تظهر بالضرورة في الاختبار. وبالتالي نريد التخلص منه ، واكتشاف توصية "أفضل الممارسات" لاستخدام الوعود مع React.

أوافق على أن المشكلات غامضة بعض الشيء ، لكن هذا يرجع إلى حد كبير إلى أنها مشكلة معقدة ما زلنا نتوصل إلى حل لها وليس لدينا استجابة جاهزة بعد.

استدعاء 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;
  }
});

هذا بالطبع هو رأيي ، ولكن يبدو لي أن تحميل البيانات غير المتزامن بوعد داخل مكون رد فعل هو سيناريو شائع يجب تغطيته بالتفاعل ، بدلاً من الاضطرار إلى كتابة التعليمات البرمجية المعيارية الخاصة بنا.

تكمن المشكلة في أنه من أجل إراحة حالة التحميل الحقيقية ، يجب أن نضيف مستمعًا عندما ينتهي التفاعل من عملية تحميل DOM في كل مكون (نفس الشيء ، الذي يرفق componentDidMount ، إذا تم تحديده) ، ولكنه سيؤثر على الأداء ، لأننا لسنا بحاجة لإراحةها في كل مكان. لا يستمع المكون إلى DOM mount جاهز افتراضيًا لأن componentDidMount غير محدد.

ماذا لو حصل setState على وعد متسلسل يحل التغييرات المطلوبة في الحالة؟ إذا تم فك المكون ، فعندئذٍ إذا كان هناك أي وعود معلقة ، فسيتم تجاهل النتيجة النهائية الخاصة بهم.

istarkov نمط لطيف ، مثل ذلك! إليك واجهة برمجة تطبيقات تم تغييرها قليلاً:

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

// cancel it
cancel();

نظرًا لأنني جديد في React وأقرأ المستندات ، فقط لطرح ذلك هناك: يستخدم تحميل البيانات الأولية عبر نصيحة .isMounted() ، لذلك لا يتفق موقع الويب مع موقع الويب. سيكون من الرائع رؤية نصيحة كاملة حول كيفية إلغاء التحميل الأولي في componentWillUnmount ، ربما باستخدام نمط istarkov أعلاه.

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 على نفس الوعد الأساسي. بشكل فعال ، هذا يخلق وعدين ، واحد يتعامل مع النجاح فقط ، وواحد يعالج الأخطاء فقط. هذا يعني أنه إذا كان هناك خطأ ، فسيتم اعتبار الوعد الأول من خلال العقدة بمثابة رفض للوعد غير المعالج.

الإصلاح سهل للغاية: ما عليك سوى ربط النداءين بحيث يقدم وعدًا واحدًا بمعالج النجاح والخطأ. هذا هو الكود الثابت:

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() بشكل متزامن لأي سبب من الأسباب وعدًا تم حله ، فسيتم استدعاء كلا المعالجين.

الحل هو استخدام النمط .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 السائدة من السهل جدًا تجنب تنظيف عمليات الاسترجاعات غير المتزامنة المعلقة.

رد اتصال واحد ليس مشكلة كبيرة. ومع ذلك ، فإن رد الاتصال هذا يتوقف على الكائنات وعمليات الاسترجاعات الوسيطة والوعود والاشتراكات. إذا قامت الكثير من المكونات الخاصة بك بذلك ، فسوف تواجه مشكلات في الذاكرة بسرعة

يقوم makeCancellable فقط بإنشاء وعد آخر ينتهي به الأمر إلى الاحتفاظ بمرجع للوظائف التي تحتوي على مرجع للمكون. الحل makeCancellable هو مجرد نقل الخاصية المنطقية إلى الوعد.

من أجل حل مشكلة GC ، يلزمك إلغاء شيء ما عند استدعاء إلغاء (). وإلا فسيظل لديك سلسلة مرجعية من العملية غير المتزامنة إلى المكون.

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 ، لذا لا أعرف كيف سيبدو. أيضًا ، هذا لا يرفض المؤجل عند استدعاء إلغاء () ولم يتم حل المؤجل. ربما يكون للناس رأي مختلف حول كيفية عمل ذلك.

لذلك تبدو السلسلة شيئًا مثل هذا:

طلب AJAX -> الإغلاق -> CancellableDef المؤجلة -> JQuery المؤجلة -> المكون

ثم بعد الإلغاء يبدو كالتالي:

طلب AJAX -> الإغلاق -> CancellableDefirmedInstance / مرجع الكائن الآن فارغ / JQuery مؤجل -> المكون

لذلك لم يعد طلب AJAX يمنع المكون من أن يكون GCd [بافتراض أنني لم أفسد التنفيذ في مكان ما من خلال الإبقاء على إشارة مؤجلة عن طريق الخطأ. إغلاق yay ....]

مرحبًا benmmurphy ، لست معتادًا جدًا على تجميع نفايات JS وقد يعمل بشكل مختلف مع React ، لكن لدي فهم مختلف.

يسمح makeCancellable بجمع مكونات React من القمامة عند فكها. سأشرح.

يقوم makeCancellable فقط بإنشاء وعد آخر ينتهي به الأمر إلى الاحتفاظ بمرجع للوظائف التي تحتوي على مرجع للمكون. الحل makeCancellable هو مجرد نقل الخاصية المنطقية إلى الوعد.

بدون makeCancellable :

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

بـ makeCancellable :

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

بدون makeCancellable لا يزال لديك مرجع إلى this لذلك لا يمكن أن يكون المكون غير مهم عند فكه. ولكن في الحالة الأخرى ، يتم استدعاء معالج فشل الوعد القابل للإلغاء بمجرد فك المكون ، لذلك لم يعد لديك أي مراجع معلقة بعد الآن.

تضمين التغريدة

لدي بعض كود nodejs الذي يوضح المشكلة. سيكون مكون Foo هو GC فقط بمجرد تعيين رد الاتصال غير المتزامن resolve على قيمة خالية. على سبيل المثال ، لنفترض أنك قمت بإطلاق طلب ajax يستغرق 30 ثانية لحل المشكلة ، ثم يتم إلغاء تثبيت المكون. ثم لن يكون المكون GCd لمدة 30 ثانية. هذه إحدى المشكلات التي يحاولون حلها باستخدام إهمال 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 هذا.

وقبل أن يستخدم أي شخص مثالي أو أي شيء مبني عليه ، تأكد من اختبار أنه في الواقع GCs بشكل صحيح. لم أختبر خاصتي بعد ولن يفاجئني إذا لم ينجح الأمر لأنني ارتكبت خطأً سخيفًا: /

من حيث API الوعد هذا يعمل بالنسبة لي في nodejs من حيث GC. على الرغم من ذلك ، أفضل عدم وجود معلمات _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 ؟ في هذه الحالة ، ستظل تعيد وعدًا جديدًا.

قبل أيام قليلة ، تمت إضافة واجهة برمجة تطبيقات جديدة إلى مواصفات DOM التي تتيح لك إحباط طلبات الجلب (). لم يتم تنفيذ واجهة برمجة التطبيقات هذه في أي متصفح حتى الآن ، لكنني قمت بإنشاء polyfill متاحًا على NPM وكان "abortcontroller-polyfill". يقوم polyfill بشكل أساسي بنفس الشيء مثل الكود المنشور بواسطة istarkov ولكنه يسمح لك بالانتقال دون أي تغييرات في التعليمات البرمجية إلى واجهة برمجة التطبيقات للمتصفح الحقيقي بمجرد تنفيذه.

التفاصيل هنا:
https://mo.github.io/2017/07/24/abort-fetch-abortcontroller-polyfill.html

نظرًا لأن React.createClass لم يعد موجودًا في React 16 وأن الحزمة الجديدة create-react-class تتضمن رسالة إهمال واضحة لـ isMounted ، سأقوم بإغلاق ذلك.

أتفق مع benmmurphy على أن حل istarkov هو نفس استخدام 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 ، يمكن ذو ترتيب أعلى (HOC) يتتبع الوعود تركيب المكون ،

تحرير: سيئتي ، لقد نظرت للتو إلى 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([originalPromise, cancelationPromise])

حل benmmurphy أقرب ولكنه

أعتقد أن الحل الخاص بي يعمل ولكني لا أعرف ما يكفي عن الوعود التي يعطيها وقت تشغيل جافا سكريبت لمعرفة ذلك على وجه اليقين. إذا قمت بتشغيل الحل تحت العقدة في اختبارك ، فاستخدم القيمة GCs بشكل صحيح. قام الحل الخاص بي بتعيين وظائف الحل / الرفض إلى نطاق أعلى ثم قام بإلغاء هذه القيم عند استدعاء إلغاء. ومع ذلك ، كانت الوظائف لا تزال متاحة في النطاق الأدنى ولكن لم تتم الإشارة إليها. أعتقد أن محركات جافا سكريبت الحديثة لا تلتقط المتغيرات في الإغلاق ما لم تتم الإشارة إليها. أعتقد أن هذه كانت مشكلة كبيرة حيث كان الأشخاص يقومون عن طريق الخطأ بإنشاء تسريبات DOM لأنهم قاموا بأشياء مثل: var element = findDOM ()؛ element.addEventListener ('click'، function () {}) ؛ وستتم الإشارة إلى العنصر في الإغلاق على الرغم من عدم استخدامه في الإغلاق.

hjylewisbenmmurphy لماذا نحن بحاجة إلى معالجات dereference ؟؟ بعد استبعاد المتعاملين ، يتم جمع القمامة بأي طريقة ، أليس كذلك؟

هذه المقتطفات من أجل "إلغاء" الوعد ليست رائعة IMHO. لن يتم حل الوعود الملغاة حتى يتم حل الوعد الأصلي. لذلك لن يحدث تنظيف الذاكرة إلا إذا استخدمت خدعة isMounted.

يجب على غلاف الوعد المناسب القابل للإلغاء أن يستفيد من الوعد والوعد الثاني. أي Promise.race([originalPromise, cancelationPromise])

hjylewis و

أعتقد أن الوعد الرافض الغريب سيستخدم Promise.race ([]) لبناء وعد قابل للإلغاء. إنه يعمل لأنه عندما يتم حل الوعد ، يتم حذف عمليات الاسترجاعات المعلقة ، لذلك عند هذه النقطة لن يكون هناك سلسلة مرجعية من شبكة المتصفح إلى المكون الخاص بك لأنه لن يكون هناك مرجع بين وعد السباق والمكون.

أشعر بالفضول لمعرفة ما إذا كان من الممكن بطريقة ما استخدام Promise.all() مع تلك الوعود القابلة للإلغاء وتجنب الأخطاء غير الملحوظة في وحدة تحكم المتصفحات ... لأنني قادر على اكتشاف خطأ الإلغاء الأول فقط ، بينما يظل الآخرون غير معلوم.

إنه 2018 ، هل هناك نهج أفضل من النهج المذكور أعلاه؟

أي نهج أفضل لإلغاء تنفيذ الوعد ، مثل setTimeout ، ومكالمات واجهة برمجة التطبيقات ، وما إلى ذلك .. إنه 2019 😭 😞

هناك سلسلة رسائل لإلغاء الوعد تجري على TC39 ، (أعتقد) أنها ذات صلة هنا (ربما .. لست متأكدًا)
https://github.com/tc39/proposal-cancellation/issues/24

أي نهج أفضل لإلغاء تنفيذ الوعد ، مثل setTimeout ، ومكالمات واجهة برمجة التطبيقات ، وما إلى ذلك .. إنه 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 التقييمات