1. ホーム
  2. ジャバスクリプト

[解決済み】ネイティブXHRをプロミス化する方法は?

2022-04-08 01:43:45

質問

私はフロントエンドアプリで(ネイティブ)プロミスを使用してXHRリクエストを実行したいのですが、巨大なフレームワークのおふざけは一切ありません。

xhrがプロミスを返すようにしたいのですが、これはうまくいきません。 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);
});

解決方法は?

ネイティブなXHRリクエストを作成する方法を知っていると仮定します。 ここで こちら )

以来 ネイティブプロミスをサポートするすべてのブラウザ をサポートします。 xhr.onload であれば、すべての onReadyStateChange のようなおふざけです。 一歩下がって、コールバックを使った基本的なXHRリクエスト関数から始めてみましょう。

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);
});

万歳! これは非常に複雑なこと(カスタムヘッダーやPOSTデータなど)ではなく、前進するのに十分なものです。

プロミスのコンストラクタ

このようにプロミスを構築することができます。

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

プロミスのコンストラクタは、2つの引数を渡される関数(ここでは resolvereject ). これらはコールバックと考えることができ、1つは成功したとき、もう1つは失敗したときです。 例はすごいですね、更新してみましょう makeRequest このコンストラクタで

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

今、私たちはプロミスの力を利用し、複数のXHRの呼び出しを連鎖させることができます(そして、その連鎖は .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 (xhr.status >= 200 && xhr.status < 300) {
        resolve(xhr.response);
      } else {
        reject({
          status: xhr.status,
          statusText: xhr.statusText
        });
      }
    };
    xhr.onerror = function () {
      reject({
        status: xhr.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);
});

より包括的なアプローチは、以下のサイトで見ることができます。 MDN .

あるいは APIを取得する ( ポリフィル ).