経験は何よりも饒舌

10年後に真価を発揮するかもしれないブログ 

axiosの内部構造を理解してadapterの仕組みを解明する

f:id:wafuwafu13:20210429171836p:plain
axiosのadapterを使う機会があり、仕組みが気になったのでメモしておく。
github.com

adapterで検索をかけると、lib/core/dispatchRequest.jsの51行目の記述が目についた。
axios/dispatchRequest.js at 7821ed20892f478ca6aea929559bd02ffcc8b063 · axios/axios · GitHub

var adapter = config.adapter || defaults.adapter;
return adapter(config).then(function onAdapterResolution(response) {
...

dispatchRequest.jsは名前からも、lib/core/Axios.jsで使われていることからも、実際にリクエストを送る部分であろう。

まずはdefaults.adapterがなんなのかを調べると、lib/defaults.jsに以下の記述があった。
axios/defaults.js at 7821ed20892f478ca6aea929559bd02ffcc8b063 · axios/axios · GitHub
axios/defaults.js at 7821ed20892f478ca6aea929559bd02ffcc8b063 · axios/axios · GitHub

function getDefaultAdapter() {
  var adapter;
  if (typeof XMLHttpRequest !== 'undefined') {
    // For browsers use XHR adapter
    adapter = require('./adapters/xhr');
  } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    // For node use HTTP adapter
    adapter = require('./adapters/http');
  }
  return adapter;
}
...
var defaults = {
    adapter: getDefaultAdapter(),
    ....

typeof XMLHttpRequestchromeのコンソールで調べると以下のようになったので、adapter = require('./adapters/xhr');の元を見にいった。

typeof XMLHttpRequest
"function"
f:id:wafuwafu13:20210429152146p:plain
typeof XMLHttpRequest = "function"

lib/adapters/xhr.jsでは、XMLHttpRequestを使ってリクエストが送られていた。

axios/xhr.js at 7821ed20892f478ca6aea929559bd02ffcc8b063 · axios/axios · GitHub
axios/xhr.js at 7821ed20892f478ca6aea929559bd02ffcc8b063 · axios/axios · GitHub
axios/xhr.js at 7821ed20892f478ca6aea929559bd02ffcc8b063 · axios/axios · GitHub

var request = new XMLHttpRequest();
...
request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
...
// Send the request
request.send(requestData);

つまり、axiosをadapterオプションを指定せずに使うと、内部的にはXMLHttpRequestを使った通信が行われ、adapterオプションを指定すると、通信そのものをラップできることが分かる。

オプションの反映はlib/core/mergeConfig.jsを使って行われていることが分かったが、深追いはせずテストコードを眺めるだけに留めた。
axios/mergeConfig.spec.js at master · axios/axios · GitHub

実際にadapterオプションを書いて使ってみた。
この場合リクエストは発生せず、実装したPromiseが返っている。

const axios = require("axios");

const sampleAdapter = () => {
  return new Promise((resolve, reject) => {
    resolve({ data: "sample data" });
  });
};

axios({
  url: "https://example.com/",
  adapter: sampleAdapter,
}).then((res) => console.log(res.data));
$ node index.js
sample data

リクエストを送る部分はlib/core配下にあるものだと思っていたので、adapterオプションを指定しない場合にlib/adapters配下のコードが使われているのが意外だった。

追記:
axiosにPR投げてみたらlib/adapters配下のコードが使われているという理解はあっていた。
github.com