আমি কীভাবে দেশীয় এক্সএইচআর প্রমিসি করব?


183

এক্সএইচআর অনুরোধটি সম্পাদন করতে আমি আমার সীমান্ত অ্যাপ্লিকেশনটিতে (দেশীয়) প্রতিশ্রুতিগুলি ব্যবহার করতে চাই তবে একটি বিশাল কাঠামোর সমস্ত টমফুলারি ছাড়াই।

আমি চাই যে আমার এক্সএইচআর একটি প্রতিশ্রুতি ফিরিয়ে দেবে তবে এটি কার্যকর হয় না (আমাকে দেওয়া Uncaught TypeError: Promise resolver undefined is not a function:)

function makeXHRRequest (method, url, done) {
  var xhr = new XMLHttpRequest();
  xhr.open(method, url);
  xhr.onload = function() { return new Promise().resolve(); };
  xhr.onerror = function() { return new Promise().reject(); };
  xhr.send();
}

makeXHRRequest('GET', 'http://example.com')
.then(function (datums) {
  console.log(datums);
});

উত্তর:


368

আমি ধরে নিচ্ছি যে আপনি কীভাবে কোনও দেশীয় এক্সএইচআর অনুরোধ করবেন (আপনি এখানে এবং এখানে ব্রাশ করতে পারেন )

যেহেতু কোন ব্রাউজার যা সমর্থন নেটিভ প্রতিশ্রুতি এছাড়াও সমর্থন করবে xhr.onload, আমরা সব এড়িয়ে যেতে পারেন onReadyStateChangeভাঁড়ামি। আসুন আমরা এক পদক্ষেপ নেব এবং কলব্যাকগুলি ব্যবহার করে একটি প্রাথমিক এক্সএইচআর অনুরোধ ফাংশন দিয়ে শুরু করব:

function makeRequest (method, url, done) {
  var xhr = new XMLHttpRequest();
  xhr.open(method, url);
  xhr.onload = function () {
    done(null, xhr.response);
  };
  xhr.onerror = function () {
    done(xhr.response);
  };
  xhr.send();
}

// And we'd call it as such:

makeRequest('GET', 'http://example.com', function (err, datums) {
  if (err) { throw err; }
  console.log(datums);
});

Hurray থেকে! এতে মারাত্মক জটিল কিছু (যেমন কাস্টম শিরোনাম বা পোষ্ট ডেটা) জড়িত না তবে আমাদের এগিয়ে যাওয়ার পক্ষে যথেষ্ট।

প্রতিশ্রুতি নির্মাণকারী

আমরা এর মতো একটি প্রতিশ্রুতি তৈরি করতে পারি:

new Promise(function (resolve, reject) {
  // Do some Async stuff
  // call resolve if it succeeded
  // reject if it failed
});

প্রতিশ্রুতিবদ্ধ নির্মাণকারী একটি ফাংশন নেয় যা দুটি আর্গুমেন্ট পাস করা হবে (আসুন তাদের কল করুন resolveএবং reject)। এগুলি আপনি কলব্যাক হিসাবে ভাবতে পারেন, একটি সাফল্যের জন্য এবং একটি ব্যর্থতার জন্য। উদাহরণ সন্ত্রস্ত যাক এর আপডেট makeRequestএই কন্সট্রাকটর সঙ্গে

function makeRequest (method, url) {
  return new Promise(function (resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.open(method, url);
    xhr.onload = function () {
      if (this.status >= 200 && this.status < 300) {
        resolve(xhr.response);
      } else {
        reject({
          status: this.status,
          statusText: xhr.statusText
        });
      }
    };
    xhr.onerror = function () {
      reject({
        status: this.status,
        statusText: xhr.statusText
      });
    };
    xhr.send();
  });
}

// Example:

makeRequest('GET', 'http://example.com')
.then(function (datums) {
  console.log(datums);
})
.catch(function (err) {
  console.error('Augh, there was an error!', err.statusText);
});

এখন আমরা একাধিক এক্সএইচআর কলগুলি শৃঙ্খলাবদ্ধ করে প্রতিশ্রুতিগুলির শক্তিতে আলতো চাপতে পারি (এবং .catchউভয় কলটিতে ত্রুটি ঘটবে):

makeRequest('GET', 'http://example.com')
.then(function (datums) {
  return makeRequest('GET', datums.url);
})
.then(function (moreDatums) {
  console.log(moreDatums);
})
.catch(function (err) {
  console.error('Augh, there was an error!', err.statusText);
});

আমরা এটি আরও আরও উন্নত করতে পারি, উভয়ই POST / PUT প্যারাম এবং কাস্টম শিরোনাম যুক্ত করে। আসুন স্বাক্ষর সহ একাধিক যুক্তির পরিবর্তে একটি বিকল্প অবজেক্ট ব্যবহার করুন:

{
  method: String,
  url: String,
  params: String | Object,
  headers: Object
}

makeRequest এখন এরকম কিছু দেখাচ্ছে:

function makeRequest (opts) {
  return new Promise(function (resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.open(opts.method, opts.url);
    xhr.onload = function () {
      if (this.status >= 200 && this.status < 300) {
        resolve(xhr.response);
      } else {
        reject({
          status: this.status,
          statusText: xhr.statusText
        });
      }
    };
    xhr.onerror = function () {
      reject({
        status: this.status,
        statusText: xhr.statusText
      });
    };
    if (opts.headers) {
      Object.keys(opts.headers).forEach(function (key) {
        xhr.setRequestHeader(key, opts.headers[key]);
      });
    }
    var params = opts.params;
    // We'll need to stringify if we've been given an object
    // If we have a string, this is skipped.
    if (params && typeof params === 'object') {
      params = Object.keys(params).map(function (key) {
        return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
      }).join('&');
    }
    xhr.send(params);
  });
}

// Headers and params are optional
makeRequest({
  method: 'GET',
  url: 'http://example.com'
})
.then(function (datums) {
  return makeRequest({
    method: 'POST',
    url: datums.url,
    params: {
      score: 9001
    },
    headers: {
      'X-Subliminal-Message': 'Upvote-this-answer'
    }
  });
})
.catch(function (err) {
  console.error('Augh, there was an error!', err.statusText);
});

আরও বিস্তৃত পদ্ধতি এমডিএন- তে পাওয়া যাবে

বিকল্পভাবে, আপনি আনতে হবে API ( পলিফিল ) ব্যবহার করতে ।


3
আপনি responseTypeপ্রমাণীকরণ, শংসাপত্রগুলির জন্য বিকল্পগুলিও যুক্ত করতে চাইতে পারেন timeout... এবং paramsঅবজেক্টগুলিতে ব্লব / বাফেরভিউ এবং FormDataদৃষ্টান্তগুলি সমর্থন করা উচিত
বার্গি

4
প্রত্যাখ্যান করার ক্ষেত্রে নতুন ত্রুটিটি ফেরানো কি ভাল হবে?
prasanthv

1
তদ্ব্যতীত, ফিরে আসার xhr.statusএবং xhr.statusTextত্রুটিযুক্ত হওয়ার কোনও অর্থ হয় না , যেহেতু সে ক্ষেত্রে তারা খালি।
dqd

2
এই কোডটি বিজ্ঞাপন হিসাবে একটি জিনিস বাদে কাজ করছে বলে মনে হচ্ছে। আমি প্রত্যাশা করেছিলাম যে জিইটি অনুরোধের সাথে প্যারামগুলি পাস করার সঠিক উপায়টি ছিল xhr.send (প্যারাম) এর মাধ্যমে। তবে, জিইটি অনুরোধগুলি প্রেরণ () পদ্ধতিতে প্রেরিত কোনও মানটিকে অগ্রাহ্য করে। পরিবর্তে, কেবলমাত্র ইউআরএলে তাদের ক্যোরিয় স্ট্রিং প্যারাম হওয়া দরকার। সুতরাং, উপরোক্ত পদ্ধতির জন্য, যদি আপনি জিইটি অনুরোধের জন্য "প্যারামগুলি" যুক্তি প্রয়োগ করতে চান, একটি জিইটি বনাম পোষ্টকে স্বীকৃতি দেওয়ার জন্য রুটিনটি সংশোধন করা দরকার এবং শর্তসাপেক্ষে এই মানগুলি এক্সএইচআর-এর হাতে দেওয়া URL এ সংযোজিত হবে .open ()।
হেয়ারবো

1
একটি ব্যবহার করা উচিত resolve(xhr.response | xhr.responseText);বেশিরভাগ ব্রাউজারে এই সময়ের মধ্যে রেসপন্স প্রতিক্রিয়া পাঠ্য হয়।
হেইনব

50

এটি নিম্নলিখিত কোডের মতো সহজ হতে পারে।

মনে রাখবেন যে এই কোডটি কেবল rejectকলব্যাক চালিত হবে যখন onerror( নেটওয়ার্ক ) বলা হবে fire ত্রুটিগুলি) বলা হবে এবং যখন এইচটিটিপি স্থিতি কোড কোনও ত্রুটি চিহ্নিত করে। এটি অন্যান্য সমস্ত ব্যতিক্রম বাদ দেয়। সেগুলি পরিচালনা করা আপনার পক্ষে উচিত, আইএমও।

অতিরিক্তভাবে, ইভেন্টটি নয় বরং rejectএকটি উদাহরণ দিয়ে কলব্যাক কল করার পরামর্শ দেওয়া Errorহয়, তবে সরলতার জন্য, আমি যেমন রেখেছি।

function request(method, url) {
    return new Promise(function (resolve, reject) {
        var xhr = new XMLHttpRequest();
        xhr.open(method, url);
        xhr.onload = resolve;
        xhr.onerror = reject;
        xhr.send();
    });
}

এবং অনুরোধ করা এটি হতে পারে:

request('GET', 'http://google.com')
    .then(function (e) {
        console.log(e.target.response);
    }, function (e) {
        // handle errors
    });

14
@ মাডারাউছিহা আমি অনুমান করি এটির টিএল; ডাঃ সংস্করণ এটি। এটি ওপিকে তাদের প্রশ্নের উত্তর দেয় এবং কেবল এটিই।
পেলেগ

এটি একটি পোস্ট অনুরোধের শরীরের কোথায় যায়?
pm এ আটকানো

1
শুধু একটি নিয়মিত XHR মত @crl:xhr.send(requestBody)
পেলগের

হ্যাঁ তবে কেন আপনি আপনার কোডটিতে সেটি অনুমতি দেননি? (যেহেতু আপনি পদ্ধতিটি প্যারাম্যাট্রাইজ করেছেন)
অক্টোব

6
আমি এই উত্তরটি পছন্দ করি কারণ এটি সঙ্গে সঙ্গে কাজ করার জন্য খুব সাধারণ কোড সরবরাহ করে যা প্রশ্নের উত্তর দেয়।
স্টিভ চ্যামিলার্ড

11

যে কেউ এখন এটি সন্ধান করে, আপনি আনতে ফাংশনটি ব্যবহার করতে পারেন । এটির বেশ ভাল সমর্থন রয়েছে

আমি প্রথমে @ সোমারকিটেন্সের উত্তরটি ব্যবহার করেছি, তবে তারপরে আবিষ্কার করেছি fetchযা এটি আমার জন্য বাক্সের বাইরে রয়েছে :)


2
পুরানো ব্রাউজারগুলি fetchফাংশনটি সমর্থন করে না , তবে গিটহাব একটি পলিফিল প্রকাশ করেছে
বিডেশম

1
fetchএটি প্রস্তাব বাতিল করব না কারণ এটি এখনও বাতিলকরণ সমর্থন করে না।
জেমস

2
ফেচ এপিআই-এর জন্য অনুমানটি এখন বাতিল করার জন্য সরবরাহ করে। সাপোর্ট এতদূর ফায়ারফক্স 57-এ প্রেরণ করে থাকে bugzilla.mozilla.org/show_bug.cgi?id=1378342 এবং এজ 16. ডেমোস: fetch-abort-demo-edge.glitch.me & mdn.github.io/dom-examples/abort -api । এবং খোলা ক্রোম এবং ওয়েবকিট বৈশিষ্ট্য বাগগুলি রয়েছে bugs.chromium.org/p/chromium/issues/detail?id=750599 এবং bugs.webkit.org/show_bug.cgi?id=174980 । কীভাবে করবেন: developers.google.com/web/updates/2017/09/abortable-fetch & developer.mozilla.org/en-US/docs/Web/API/bortSignal#
উদাহরণ

এ উত্তর stackoverflow.com/questions/31061838/... হয়েছে cancelable-আনা কোড উদাহরণ এতদূর ইতিমধ্যে ফায়ারফক্স 57+ এবং এজ 16+ কাজ করে
sideshowbarker

বাতিল হওয়া আনয়ন আমাকে ক্রোম in৯ এ এখনও সমস্যা দিচ্ছে X
ম্যাট মন্টাগ

8

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

এই ফাংশনটি একটি স্বেচ্ছাসেবী XMLHttpRequestবস্তুকে একটি প্রতিশ্রুতিতে রূপান্তর করে , অ -200 স্থিতি কোডটিকে ডিফল্টরূপে ত্রুটি হিসাবে বিবেচনা করে:

function promiseResponse(xhr, failNon2xx = true) {
    return new Promise(function (resolve, reject) {
        // Note that when we call reject, we pass an object
        // with the request as a property. This makes it easy for
        // catch blocks to distinguish errors arising here
        // from errors arising elsewhere. Suggestions on a 
        // cleaner way to allow that are welcome.
        xhr.onload = function () {
            if (failNon2xx && (xhr.status < 200 || xhr.status >= 300)) {
                reject({request: xhr});
            } else {
                resolve(xhr);
            }
        };
        xhr.onerror = function () {
            reject({request: xhr});
        };
        xhr.send();
    });
}

এই ফাংশনটি খুব সহজেই এপিআইয়ের Promiseনমনীয়তা ত্যাগ না করে খুব সহজেই একটি শৃঙ্খলার সাথে ফিট করে XMLHttpRequest:

Promise.resolve()
.then(function() {
    // We make this a separate function to avoid
    // polluting the calling scope.
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://stackoverflow.com/');
    return xhr;
})
.then(promiseResponse)
.then(function(request) {
    console.log('Success');
    console.log(request.status + ' ' + request.statusText);
});

catchনমুনা কোডটি সহজ রাখার জন্য উপরে বাদ দেওয়া হয়েছিল। আপনার সর্বদা একটি থাকা উচিত, এবং অবশ্যই আমরা তা করতে পারি:

Promise.resolve()
.then(function() {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://stackoverflow.com/doesnotexist');
    return xhr;
})
.then(promiseResponse)
.catch(function(err) {
    console.log('Error');
    if (err.hasOwnProperty('request')) {
        console.error(err.request.status + ' ' + err.request.statusText);
    }
    else {
        console.error(err);
    }
});

এবং এইচটিটিপি স্থিতি কোড হ্যান্ডলিং অক্ষম করার জন্য কোডে খুব বেশি পরিবর্তন প্রয়োজন হয় না:

Promise.resolve()
.then(function() {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://stackoverflow.com/doesnotexist');
    return xhr;
})
.then(function(xhr) { return promiseResponse(xhr, false); })
.then(function(request) {
    console.log('Done');
    console.log(request.status + ' ' + request.statusText);
});

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

আমাদের কোডটি পরিষ্কার করার জন্য আমরা কয়েকটি সুবিধাজনক ফাংশন যুক্ত করতে পারি:

function makeSimpleGet(url) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    return xhr;
}

function promiseResponseAnyCode(xhr) {
    return promiseResponse(xhr, false);
}

তারপরে আমাদের কোডটি হয়ে যায়:

Promise.resolve(makeSimpleGet('https://stackoverflow.com/doesnotexist'))
.then(promiseResponseAnyCode)
.then(function(request) {
    console.log('Done');
    console.log(request.status + ' ' + request.statusText);
});

5

jpmc26 এর উত্তর আমার মতে নিখুঁত কাছাকাছি। যদিও এর কিছু ত্রুটি রয়েছে:

  1. এটি কেবলমাত্র শেষ মুহুর্ত পর্যন্ত এক্সএইচআর অনুরোধটি প্রকাশ করে। এটি- POSTঅনুরোধগুলি বডি সেট করার অনুমতি দেয় না ।
  2. sendক্রিয়াল-কলটি কোনও ফাংশনের অভ্যন্তরে লুকানো থাকায় এটি পড়া শক্ত ।
  3. বাস্তবে অনুরোধ করার সময় এটি বেশ কিছুটা বয়লারপ্লেট পরিচিত করে।

এক্সএইচআর-অবজেক্টে প্যাচিং করা বানর এই সমস্যাগুলি মোকাবেলা করে:

function promisify(xhr, failNon2xx=true) {
    const oldSend = xhr.send;
    xhr.send = function() {
        const xhrArguments = arguments;
        return new Promise(function (resolve, reject) {
            // Note that when we call reject, we pass an object
            // with the request as a property. This makes it easy for
            // catch blocks to distinguish errors arising here
            // from errors arising elsewhere. Suggestions on a 
            // cleaner way to allow that are welcome.
            xhr.onload = function () {
                if (failNon2xx && (xhr.status < 200 || xhr.status >= 300)) {
                    reject({request: xhr});
                } else {
                    resolve(xhr);
                }
            };
            xhr.onerror = function () {
                reject({request: xhr});
            };
            oldSend.apply(xhr, xhrArguments);
        });
    }
}

এখন ব্যবহারের মতো সহজ:

let xhr = new XMLHttpRequest()
promisify(xhr);
xhr.open('POST', 'url')
xhr.setRequestHeader('Some-Header', 'Some-Value')

xhr.send(resource).
    then(() => alert('All done.'),
         () => alert('An error occured.'));

অবশ্যই, এটি একটি পৃথক ত্রুটি দেখা দেয়: বানর-প্যাচিং ক্ষতিগ্রস্থ পারফরম্যান্সকে ক্ষতি করে। তবে এটি ধরে নিতে সমস্যা হওয়া উচিত নয় যে ব্যবহারকারী প্রধানত xhr এর ফলাফলের জন্য অপেক্ষা করছেন, অনুরোধটি নিজেই কল সেট করার চেয়ে দীর্ঘতরতার অর্ডার নেয় এবং xhr অনুরোধগুলি প্রায়শই প্রেরণ করা হয় না।

PS: এবং অবশ্যই যদি আধুনিক ব্রাউজারগুলিকে লক্ষ্য করে তোলা হয় তবে আনুন!

পিপিএস: মন্তব্যে ইঙ্গিত করা হয়েছে যে এই পদ্ধতিটি স্ট্যান্ডার্ড এপিআই পরিবর্তন করে যা বিভ্রান্তিকর হতে পারে। আরও সুস্পষ্টতার জন্য xhr অবজেক্টে একটি পৃথক পদ্ধতি প্যাচ করতে পারে sendAndGetPromise()


আমি বানরের প্যাচিং এড়াচ্ছি কারণ এটি আশ্চর্যজনক। বেশিরভাগ বিকাশকারী আশা করেন যে স্ট্যান্ডার্ড এপিআই ফাংশন নামগুলি স্ট্যান্ডার্ড এপিআই ফাংশনটি শুরু করে। এই কোডটি এখনও প্রকৃত sendকলটি লুকিয়ে রাখে তবে এমন পাঠকদের বিভ্রান্ত করতে পারে যারা জানেন যে sendকোনও রিটার্নের মূল্য নেই। আরও সুস্পষ্ট কলগুলি ব্যবহার করা আরও স্পষ্ট করে তোলে যে অতিরিক্ত যুক্তি চাওয়া হয়েছে। আমার উত্তরটিতে যুক্তিগুলি পরিচালনা করতে সামঞ্জস্য করা দরকার send; তবে সম্ভবত এটি fetchএখন ব্যবহার করা ভাল ।
jpmc26

আমার ধারণা এটি নির্ভর করে। আপনি যদি এক্সএইচআর অনুরোধটি ফিরিয়ে / এক্সপোজ করেন (যা কোনওরকম সন্দেহজনক মনে হয়) আপনি একেবারে ঠিক। তবে আমি দেখতে পাচ্ছি না কেন কোনও ব্যক্তি মডিউলটির মধ্যে এটি না করে কেবল ফলাফল প্রতিশ্রুতি প্রকাশ করে।
t.animal

আপনি যে
কোডটিতে

যেমন আমি বলেছিলাম: এটি নির্ভর করে। যদি আপনার মডিউলটি এত বিশাল হয় যে বাকী কোডগুলির মধ্যে প্রতিশ্রুতিবদ্ধ ফাংশনটি হারিয়ে যায় আপনি সম্ভবত অন্যান্য সমস্যা পেয়েছেন। যদি আপনি এমন একটি মডিউল পেয়ে থাকেন যেখানে আপনি কেবল কিছু শেষ পয়েন্ট কল করতে চান এবং প্রতিশ্রুতি ফিরে চান তবে আমি কোনও সমস্যা দেখছি না।
t.animal

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