প্রতিশ্রুতি - একটি প্রতিশ্রুতি বাতিল জোর করা সম্ভব?


91

আমি আমার সমস্ত নেটওয়ার্ক ডেটা পুনরুদ্ধার পরিচালনা করতে ES6 প্রতিশ্রুতি ব্যবহার করি এবং এমন কিছু পরিস্থিতি রয়েছে যেখানে আমাকে সেগুলি জোর করে বাতিল করতে হবে।

মূলত দৃশ্যটি এমন যে আমার কাছে ইউআইতে টাইপ-ফরোয়ার্ড অনুসন্ধান রয়েছে যেখানে অনুরোধটি ব্যাকএন্ডে অর্পণ করা হয়েছে আংশিক ইনপুটের ভিত্তিতে অনুসন্ধান চালিয়ে যেতে হবে। যদিও এই নেটওয়ার্কের অনুরোধটি (# 1) কিছুটা সময় নিতে পারে, ব্যবহারকারী টাইপ করতে থাকে যা অবশেষে অন্য একটি ব্যাকএন্ড কল (# 2) ট্রিগার করে continues

এখানে # 2 স্বাভাবিকভাবেই # 1 এর চেয়ে বেশি অগ্রাধিকার নেয় তাই আমি প্রতিশ্রুতি মোড়ানোর অনুরোধ # 1 বাতিল করতে চাই। আমার কাছে ইতিমধ্যে ডেটা লেয়ারে সমস্ত প্রতিশ্রুতিগুলির একটি ক্যাশ রয়েছে যাতে আমি # 2 এর জন্য প্রতিশ্রুতি জমা দেওয়ার চেষ্টা করার কারণে আমি তাত্ত্বিকভাবে এটি পুনরুদ্ধার করতে পারি।

কিন্তু একবার আমি ক্যাশে থেকে পুনরুদ্ধার করার পরে আমি প্রতিশ্রুতি # 1 কীভাবে বাতিল করব?

কেউ কি কোনও পদ্ধতির পরামর্শ দিতে পারে?


4
প্রায়শই ট্রিগার না করা এবং অপ্রচলিত অনুরোধ হয়ে ওঠার জন্য না কোনও ডিবিউন ফাংশনটির কিছু সমতুল্য ব্যবহার করার বিকল্পটি কী? বলুন 300 এমএস বিলম্বের কৌশলটি করবে। উদাহরণস্বরূপ লোদাশের একটি বাস্তবায়ন রয়েছে - লড্যাশ
ডকস #

এটি তখন যখন বেকন এবং আরএক্সের মতো জিনিস কার্যকর হয়।
এলকাররা

@ শার্শান হ্যাঁ - আমাদের এটি আছে তবে এটি ইউআই ইস্যু সম্পর্কে এতটা নয় ... সার্ভারের ক্যোয়ারিতে কিছুটা সময় লাগতে পারে তাই আমি প্রতিশ্রুতিগুলি বাতিল করতে সক্ষম হতে চাই ...
মুনওয়ালকার


Rxjs থেকে পর্যবেক্ষণগুলি চেষ্টা করুন
ফাইরিকড

উত্তর:


164

না। আমরা এখনও এটি করতে পারি না।

ES6 প্রতিশ্রুতি বাতিল এখনও সমর্থন করে না । এটি চলছে, এবং এর নকশা এমন একটি জিনিস যা প্রচুর লোকেরা সত্যই কঠোর পরিশ্রম করেছিল। শব্দ বাতিলকরণ শব্দার্থ সঠিকভাবে পাওয়া শক্ত এবং এটি কাজ চলছে। "ফেচ" রেপোতে, এসডিস্কাসে এবং জিএইচ-তে বেশ কয়েকটি রেপো নিয়ে আকর্ষণীয় বিতর্ক রয়েছে তবে আমি আপনি থাকলে আমি কেবল ধৈর্য ধরতাম be

তবে, তবে, তবে .. বাতিল করা সত্যিই গুরুত্বপূর্ণ!

এটা, বিষয়টি বাস্তবতা বাতিলের হয় সত্যিই ক্লায়েন্ট-সাইড প্রোগ্রামিং গুরুত্বপূর্ণ দৃশ্যকল্প। ওয়েব অনুরোধগুলি বাতিল করার মতো বিবরণগুলি আপনার কাছে গুরুত্বপূর্ণ এবং সেগুলি সর্বত্র রয়েছে।

সুতরাং ... ভাষা আমাকে খারাপ!

হ্যাঁ, দুঃখিত। প্রতিশ্রুতিগুলি অন্যান্য জিনিসগুলি নির্দিষ্ট করার আগে প্রথমে প্রবেশ করতে হয়েছিল - সুতরাং তারা কোনও দরকারী জিনিস ছাড়াই প্রবেশ করেছিল .finallyএবং .cancel- এটি ডিওএমের মাধ্যমে অনুমান করার পথে though বাতিল হয় না একটি অনুচিন্তা এটি শুধু একটি সময় বাধ্যতা এবং API নকশা আরো একটি পুনরাবৃত্ত প্রবেশপথ।

তাই আমি কি করতে পারি?

আপনার বেশ কয়েকটি বিকল্প রয়েছে:

  • ব্লুবার্ডের মতো একটি তৃতীয় পক্ষের লাইব্রেরি ব্যবহার করুন যিনি অনুমানের তুলনায় অনেক দ্রুত এগিয়ে যেতে পারেন এবং বাতিলকরণের পাশাপাশি আরও কিছু গুডিও করতে পারেন - হোয়াটসঅ্যাপের মতো বড় সংস্থাগুলি এটি করে।
  • একটি বাতিলকরণ টোকেন পাস করুন ।

তৃতীয় পক্ষের লাইব্রেরি ব্যবহার করা বেশ সুস্পষ্ট। একটি টোকেন হিসাবে, আপনি আপনার পদ্ধতিতে কোনও ফাংশন গ্রহণ করতে এবং তারপরে এটিকে কল করতে পারেন:

function getWithCancel(url, token) { // the token is for cancellation
   var xhr = new XMLHttpRequest;
   xhr.open("GET", url);
   return new Promise(function(resolve, reject) {
      xhr.onload = function() { resolve(xhr.responseText); });
      token.cancel = function() {  // SPECIFY CANCELLATION
          xhr.abort(); // abort request
          reject(new Error("Cancelled")); // reject the promise
      };
      xhr.onerror = reject;
   });
};

যা আপনাকে করতে দেয়:

var token = {};
var promise = getWithCancel("/someUrl", token);

// later we want to abort the promise:
token.cancel();

আপনার আসল ব্যবহারের কেস - last

টোকেন পদ্ধতির সাথে এটি খুব কঠিন নয়:

function last(fn) {
    var lastToken = { cancel: function(){} }; // start with no op
    return function() {
        lastToken.cancel();
        var args = Array.prototype.slice.call(arguments);
        args.push(lastToken);
        return fn.apply(this, args);
    };
}

যা আপনাকে করতে দেয়:

var synced = last(getWithCancel);
synced("/url1?q=a"); // this will get canceled 
synced("/url1?q=ab"); // this will get canceled too
synced("/url1?q=abc");  // this will get canceled too
synced("/url1?q=abcd").then(function() {
    // only this will run
});

এবং না, বেকন এবং আরএক্সের মতো লাইব্রেরিগুলি এখানে "জ্বলজ্বল করে না" কারণ তারা পর্যবেক্ষণযোগ্য লাইব্রেরি, তারা কেবল একই স্তরের ব্যবহারকারীর স্তরের প্রতিশ্রুতি গ্রন্থাগারগুলিতে নির্দিষ্ট সীমাবদ্ধ না হয়ে থাকে। আমার ধারণা, আমরা পর্যবেক্ষকরা দেশীয় হয়ে গেলে ES2016 এ অপেক্ষা করব এবং দেখব। তারা হয় Typeahead যদিও জন্য ছিমছাম।


25
বেনিয়ামিন, আপনার উত্তরটি পড়ে সত্যিই ভাল লাগল। খুব ভাল চিন্তিত, কাঠামোগত, উচ্চারণযোগ্য এবং ভাল ব্যবহারিক উদাহরণ এবং বিকল্পগুলির সাথে। সত্যিই সহায়ক। ধন্যবাদ.
মুনওয়াক্কার

@ ফ্রানসিস্কোপ্রেনসিয়া বাতিলকরণ টোকেনগুলি প্রথম ধাপের প্রস্তাব হিসাবে চলছে।
বেনিয়ামিন গ্রুইনবাউম

এই টোকেন ভিত্তিক বাতিলকরণটি আমরা কোথায় পড়তে পারি? প্রস্তাব কোথায়?
ক্ষতি করুন

@ হরম প্রস্তাবটি প্রথম পর্যায়ে মারা গেছে
বেঞ্জামিন গ্রুইনবাউম

4
রনের কাজটি আমি পছন্দ করি তবে আমি মনে করি যে গ্রন্থাগারীরা এখনও ব্যবহার করছে না তাদের জন্য সুপারিশ করার আগে আমাদের কিছুটা অপেক্ষা করা উচিত:] লিঙ্কটির জন্য ধন্যবাদ যদিও আমি এটি যাচাই করব!
বেনিয়ামিন গ্রুইনবাউম

24

বাতিলযোগ্য প্রতিশ্রুতিগুলির জন্য স্ট্যান্ডার্ড প্রস্তাবগুলি ব্যর্থ হয়েছে।

অ্যাসিঙ্ক অ্যাকশনটি পূরণের জন্য একটি প্রতিশ্রুতি নিয়ন্ত্রণ ব্যবস্থা নয়; ভোক্তার সাথে মালিককে বিভ্রান্ত করে। পরিবর্তে, অ্যাসিক্রোনাস ফাংশন তৈরি করুন যা কিছু পাস-ইন টোকেনের মাধ্যমে বাতিল হতে পারে।

আর একটি প্রতিশ্রুতি একটি জরিমানা টোকেন তৈরি করে, এর সাথে বাস্তবায়ন করা সহজ করে Promise.race:

উদাহরণ:Promise.race পূর্ববর্তী শৃঙ্খলার প্রভাব বাতিল করতে ব্যবহার করুন :

let cancel = () => {};

input.oninput = function(ev) {
  let term = ev.target.value;
  console.log(`searching for "${term}"`);
  cancel();
  let p = new Promise(resolve => cancel = resolve);
  Promise.race([p, getSearchResults(term)]).then(results => {
    if (results) {
      console.log(`results for "${term}"`,results);
    }
  });
}

function getSearchResults(term) {
  return new Promise(resolve => {
    let timeout = 100 + Math.floor(Math.random() * 1900);
    setTimeout(() => resolve([term.toLowerCase(), term.toUpperCase()]), timeout);
  });
}
Search: <input id="input">

এখানে আমরা কোনও undefinedফলাফল ইনজেকশনের মাধ্যমে এবং এর জন্য পরীক্ষার মাধ্যমে পূর্ববর্তী অনুসন্ধানগুলিকে "বাতিল" করছি , তবে এর "CancelledError"পরিবর্তে আমরা সহজেই প্রত্যাখ্যান করতে পারি ।

অবশ্যই এটি নেটওয়ার্ক অনুসন্ধানটি বাতিল করে না, তবে এটি একটি সীমাবদ্ধতা fetch। যদি fetchযুক্তি হিসাবে বাতিল প্রতিশ্রুতি গ্রহণ করা হয়, তবে এটি নেটওয়ার্ক ক্রিয়াকলাপ বাতিল করতে পারে।

আমি এস-আলোচনার জন্য এই "প্রতিশ্রুতি রীতি বাতিল করুন" প্রস্তাব করেছি , ঠিক এটি করার পরামর্শ দেওয়ার জন্য fetch


@ জিব কেন আমার সংশোধন বাতিল করবেন? আমি শুধু এটি স্পষ্ট করতে।
অ্যালেনাইলি

8

আমি মজিলা জেএস রেফারেন্সটি পরীক্ষা করে দেখেছি এবং এটি পেয়েছি:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/ উল্লেখ / গ্লোবাল_অবজেক্টস / প্রমিজ / প্যারা

আসুন এটি পরীক্ষা করে দেখুন:

var p1 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 500, "one"); 
});
var p2 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 100, "two"); 
});

Promise.race([p1, p2]).then(function(value) {
  console.log(value); // "two"
  // Both resolve, but p2 is faster
});

আমাদের কাছে এখানে পি 1, এবং পি 2 রয়েছে Promise.race(...)যুক্তি হিসাবে, এটি আসলে নতুন সংকল্প প্রতিশ্রুতি তৈরি করছে, যা আপনার প্রয়োজন require


নিস - এটি সম্ভবত আমার প্রয়োজন ঠিক। আমি একবার চেষ্টা করবো।
মুনওয়াক্কার

এতে আপনার যদি সমস্যা হয় তবে আপনি কোডটি এখানে পেস্ট করতে পারেন যাতে আমি আপনাকে সহায়তা করতে পারি :)
নিকোলা-মিলজকোভিক

4
এটা চেষ্টা করেছি. বেশ সেখানে নেই। এটি দ্রুততম প্রতিশ্রুতি সমাধান করে ... আমার সর্বদা সর্বশেষ জমা দেওয়া সমাধান করা দরকার অর্থাত্ কোনও পুরানো প্রতিশ্রুতি নিঃশর্তভাবে বাতিল করতে হবে ..
মুনওয়াককার

4
এইভাবে অন্য সমস্ত প্রতিশ্রুতিগুলি আর পরিচালনা করা হয় না, আপনি আসলে কোনও প্রতিশ্রুতি বাতিল করতে পারবেন না।
নিকোলা-মিলজকোভিক

আমি চেষ্টা করেছিলাম, দ্বিতীয় প্রতিশ্রুতি (এই প্রাক্তনের মধ্যে একটি) প্রক্রিয়াটি প্রস্থান করতে দেবে না :(
মোর্তজা আতাই

3

নোড.জেএস এবং ইলেকট্রনের জন্য আমি জাভাস্ক্রিপ্ট (প্রেক্স) এর প্রতিশ্রুতি এক্সটেনশনগুলি ব্যবহার করার জন্য সুপারিশ করব । এর লেখক রন বাক্টন মূল প্রকারের স্ক্রিপ্ট ইঞ্জিনিয়ারদের একজন এবং বর্তমান টিসি 39 এর ইসমাস্ক্রিপ্ট বাতিল প্রস্তাবের পিছনে লোকও । লাইব্রেরিটি ভালভাবে নথিভুক্ত এবং সম্ভাবনা রয়েছে প্রেক্সের কিছু মানক to

ব্যক্তিগত নোট এবং সি # ব্যাকগ্রাউন্ড থেকে আসার সময়, আমি খুব সত্যটি পছন্দ করি যে পরিচালিত থ্রেড ফ্রেমওয়ার্কের বিদ্যমান বাতিলকরণের ভিত্তিতে প্রেক্সকে মডেল করা হয়েছে , অর্থাৎ CancellationTokenSource/। CancellationTokenনেট এপিআইয়ের সাথে নেওয়া পদ্ধতির উপর ভিত্তি করে । আমার অভিজ্ঞতায়, এগুলি পরিচালিত অ্যাপ্লিকেশনগুলিতে শক্তিশালী বাতিল যুক্তি প্রয়োগ করতে খুব কার্যকর।

ব্রাফিকফি ব্যবহার করে প্রেক্স বান্ডিল করে এটি ব্রাউজারের মধ্যে কাজ করার জন্যও যাচাই করেছি ।

বাতিলকরণের সাথে বিলম্বের উদাহরণ এখানে রয়েছে ( গিস্ট এবং রানকিট , এর জন্য প্রেক্স ব্যবহার করে CancellationTokenএবং Deferred):

// by @noseratio
// https://gist.github.com/noseratio/141a2df292b108ec4c147db4530379d2
// https://runkit.com/noseratio/cancellablepromise

const prex = require('prex');

/**
 * A cancellable promise.
 * @extends Promise
 */
class CancellablePromise extends Promise {
  static get [Symbol.species]() { 
    // tinyurl.com/promise-constructor
    return Promise; 
  }

  constructor(executor, token) {
    const withCancellation = async () => {
      // create a new linked token source 
      const linkedSource = new prex.CancellationTokenSource(token? [token]: []);
      try {
        const linkedToken = linkedSource.token;
        const deferred = new prex.Deferred();
  
        linkedToken.register(() => deferred.reject(new prex.CancelError()));
  
        executor({ 
          resolve: value => deferred.resolve(value),
          reject: error => deferred.reject(error),
          token: linkedToken
        });

        await deferred.promise;
      } 
      finally {
        // this will also free all linkedToken registrations,
        // so the executor doesn't have to worry about it
        linkedSource.close();
      }
    };

    super((resolve, reject) => withCancellation().then(resolve, reject));
  }
}

/**
 * A cancellable delay.
 * @extends Promise
 */
class Delay extends CancellablePromise {
  static get [Symbol.species]() { return Promise; }

  constructor(delayMs, token) {
    super(r => {
      const id = setTimeout(r.resolve, delayMs);
      r.token.register(() => clearTimeout(id));
    }, token);
  }
}

// main
async function main() {
  const tokenSource = new prex.CancellationTokenSource();
  const token = tokenSource.token;
  setTimeout(() => tokenSource.cancel(), 2000); // cancel after 2000ms

  let delay = 1000;
  console.log(`delaying by ${delay}ms`); 
  await new Delay(delay, token);
  console.log("successfully delayed."); // we should reach here

  delay = 2000;
  console.log(`delaying by ${delay}ms`); 
  await new Delay(delay, token);
  console.log("successfully delayed."); // we should not reach here
}

main().catch(error => console.error(`Error caught, ${error}`));

দ্রষ্টব্য যে বাতিলকরণ একটি প্রতিযোগিতা। উদাহরণস্বরূপ, একটি প্রতিশ্রুতি সফলভাবে সমাধান হয়ে গেছে, তবে আপনি এটি পর্যবেক্ষণ করার সাথে ( awaitবা সাথে then), বাতিলকরণটিও ট্রিগার হতে পারে। আপনি এই দৌড়টিকে কীভাবে পরিচালনা করেন তা আপনার উপর নির্ভর করে তবে token.throwIfCancellationRequested()আমি উপরে যেমন করলাম তেমন কোনও অতিরিক্ত সময় কল করতে ব্যথা হয় না ।


1

আমি সম্প্রতি একই ধরণের সমস্যার মুখোমুখি হয়েছি।

আমার কাছে প্রতিশ্রুতি ভিত্তিক ক্লায়েন্ট ছিল (কোনও নেটওয়ার্ক নেই) এবং আমি সর্বদা ইউআইকে মসৃণ রাখতে সর্বশেষতম অনুরোধ করা ডেটা ব্যবহারকারীকে দিতে চাইতাম।

বাতিলকরণ আইডিয়াটির সাথে লড়াই করার পরে, Promise.race(...)এবং Promise.all(..)আমি কেবল আমার শেষ অনুরোধ আইডিটি মনে করতে শুরু করেছি এবং যখন প্রতিশ্রুতি পূর্ণ হয়েছিল আমি কেবল তখনই আমার ডেটা উপস্থাপন করতাম যখন এটি কোনও শেষ অনুরোধের আইডিটির সাথে মিলে যায়।

আশা করি এটি কাউকে সাহায্য করবে।


স্লোমস্কি প্রশ্নটি ইউআইতে কী প্রদর্শিত হবে তা নয়। প্রতিশ্রুতি বাতিল করার বিষয়ে এটি
সাইবারআভায়


0

আপনি প্রতিশ্রুতি শেষ করার আগে প্রত্যাখ্যান করতে পারেন:

// Our function to cancel promises receives a promise and return the same one and a cancel function
const cancellablePromise = (promiseToCancel) => {
  let cancel
  const promise = new Promise((resolve, reject) => {
    cancel = reject
    promiseToCancel
      .then(resolve)
      .catch(reject)
  })
  return {promise, cancel}
}

// A simple promise to exeute a function with a delay
const waitAndExecute = (time, functionToExecute) => new Promise((resolve, reject) => {
  timeInMs = time * 1000
  setTimeout(()=>{
    console.log(`Waited ${time} secs`)
    resolve(functionToExecute())
  }, timeInMs)
})

// The promise that we will cancel
const fetchURL = () => fetch('https://pokeapi.co/api/v2/pokemon/ditto/')

// Create a function that resolve in 1 seconds. (We will cancel it in 0.5 secs)
const {promise, cancel} = cancellablePromise(waitAndExecute(1, fetchURL))

promise
  .then((res) => {
    console.log('then', res) // This will executed in 1 second
  })
  .catch(() => {
    console.log('catch') // We will force the promise reject in 0.5 seconds
  })

waitAndExecute(0.5, cancel) // Cancel previous promise in 0.5 seconds, so it will be rejected before finishing. Commenting this line will make the promise resolve

দুর্ভাগ্যক্রমে আনার কলটি ইতিমধ্যে হয়ে গেছে, সুতরাং আপনি কলটি নেটওয়ার্ক ট্যাবে সমাধান করতে দেখবেন। আপনার কোড কেবল এটিকে উপেক্ষা করবে।


0

বাহ্যিক প্যাকেজ সরবরাহিত প্রতিশ্রুতি সাবক্লাস ব্যবহার করে, এটি নিম্নলিখিত হিসাবে করা যেতে পারে: লাইভ ডেমো

import CPromise from "c-promise2";

function fetchWithTimeout(url, {timeout, ...fetchOptions}= {}) {
    return new CPromise((resolve, reject, {signal}) => {
        fetch(url, {...fetchOptions, signal}).then(resolve, reject)
    }, timeout)
}

const chain= fetchWithTimeout('http://localhost/')
    .then(response => response.json())
    .then(console.log, console.warn);

//chain.cancel(); call this to abort the promise and releated request

-1

কারণ @ জিব আমার পরিবর্তনটি প্রত্যাখ্যান করে, তাই আমি আমার উত্তর এখানে পোস্ট করি। এটি কেবলমাত্র কিছু মন্তব্য এবং আরও বোধগম্য পরিবর্তনশীল নাম ব্যবহার করে @ জিবের আনসারকে পরিবর্তন করেছে।

নীচে আমি কেবল দুটি পৃথক পদ্ধতির উদাহরণ দেখাব: একটি হল সমাধান () অন্যটি প্রত্যাখ্যানযোগ্য ()

let cancelCallback = () => {};

input.oninput = function(ev) {
  let term = ev.target.value;
  console.log(`searching for "${term}"`);
  cancelCallback(); //cancel previous promise by calling cancelCallback()

  let setCancelCallbackPromise = () => {
    return new Promise((resolve, reject) => {
      // set cancelCallback when running this promise
      cancelCallback = () => {
        // pass cancel messages by resolve()
        return resolve('Canceled');
      };
    })
  }

  Promise.race([setCancelCallbackPromise(), getSearchResults(term)]).then(results => {
    // check if the calling of resolve() is from cancelCallback() or getSearchResults()
    if (results == 'Canceled') {
      console.log("error(by resolve): ", results);
    } else {
      console.log(`results for "${term}"`, results);
    }
  });
}


input2.oninput = function(ev) {
  let term = ev.target.value;
  console.log(`searching for "${term}"`);
  cancelCallback(); //cancel previous promise by calling cancelCallback()

  let setCancelCallbackPromise = () => {
    return new Promise((resolve, reject) => {
      // set cancelCallback when running this promise
      cancelCallback = () => {
        // pass cancel messages by reject()
        return reject('Canceled');
      };
    })
  }

  Promise.race([setCancelCallbackPromise(), getSearchResults(term)]).then(results => {
    // check if the calling of resolve() is from cancelCallback() or getSearchResults()
    if (results !== 'Canceled') {
      console.log(`results for "${term}"`, results);
    }
  }).catch(error => {
    console.log("error(by reject): ", error);
  })
}

function getSearchResults(term) {
  return new Promise(resolve => {
    let timeout = 100 + Math.floor(Math.random() * 1900);
    setTimeout(() => resolve([term.toLowerCase(), term.toUpperCase()]), timeout);
  });
}
Search(use resolve): <input id="input">
<br> Search2(use reject and catch error): <input id="input2">

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.