Ajax、Axios、Fetch的核心区别
Ajax:前后端数据通信(同源、跨域)。 正常的Ajax操作:
let xhr = new XMLHttpRequest();
xhr.open("get", "http://127.0.0.1:8888");
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
let text = xhr.responseText;
console.log(JSON.parse(text));
}
};
xhr.send();
但是每次我们使用的时候都通过这四步,很麻烦,所以需要对这四步操作进行封装。
axios是对Ajax的封装,是基于Promise管理请求
- 解决了回调地狱的问题,结合await使用
(async function () {
let result = await axios.post('/user/login', {
account: 'XiaoMing',
password: md5('1234567890')
});
result = await axios.get('/user/list');
console.log(result)
})();
Fetch是ES6新增的通信方法,不是ajax,但是它本身实现数据通信,就是基于Promise管理的。
- 在使用过程中你会发现,我们用fetch不用引入任何东西,直接使用即可,但是每次拿到响应都需要
response.json(),所以还需要进一步封装。 - fetch认为状态码不只是200为成功,400、500...只要服务器有返回都算成功。
(async function () {
let result = await fetch('http://127.0.0.1:8888/user/login', {
method: 'post,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
body: Qs.stringify({
account: 'XiaoMing',
password: md5('1234567890')
})
}).then(response => {
return response.json();
})
console.log(result)
result = await fetch('http://127.0.0.1:8888/user/list').then(response => {
return response.join();
});
console.log(result)
})();
总结:Ajax/Axios是一个玩意,JQ中的Ajax和axios都是基于XMLHttpRequest对Ajax进行封装,一个是基于promise一个是基于回调函数,fetch和它们都不一样它天生就是一个自带类,通过它就能发请求,基于promise管理,是浏览器提供的一种通信方案。
基于Promise.all实现Ajax的串行和并行
串行: 请求是异步的,需要等待上一个请求成功,才能执行下一个请求
这样的需求在项目中很常见,比如登录逻辑。
并行: 同时发送多个请求(HTTP请求可以同时进行,但是JS的操作都是一步步来的,因为JS是单线程),等待所有请求都成功,我们再去做什么事情...
Promise.all([
axios.get('/user/list'),
axios.get('/user/list'),
axios.get('/user/list'),
axios.get('/user/list')
]).then(results => {
console.log(results)
}).catch(reason => {
})
如何控制多个并发请求?
并发限制指的是,每个时刻并发执行的promises数量是固定的,最终的执行结果还是保持与原来的一致
const delay = function delay(interval) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(interval);
}, interval);
});
};
let tasks = [
() => {
return delay(1000);
},
() => {
return delay(1003);
},
() => {
return delay(1005);
},
() => {
return delay(1002);
},
() => {
return delay(1004);
},
() => {
return delay(1006);
},
];
Promise.all(tasks.map((task) => task())).then((results) => {
console.log(results);
});
// [ 1000, 1003, 1005, 1002, 1004, 1006 ]
但是这样实现是没有并发限制的。
解决方案一:
思路:
- 并发几个就创建几个工作区,只要有任何一个工作区失败,整体就失败。
- 工作区分别执行请求,成功一个紧接着添加下一个请求到工作区,一直到所有任务都处理完毕。 tasks数组,数组包含很多方法,每一个方法执行就是发送一个请求(基于Promise管理)
const delay = function delay(interval) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(interval);
}, interval);
});
};
let tasks = [
() => {
return delay(1000);
},
() => {
return delay(1003);
},
() => {
return delay(1005);
},
() => {
return delay(1002);
},
() => {
return delay(1004);
},
() => {
return delay(1006);
},
() => {
return delay(1007);
},
];
function createRequest(tasks, pool) {
pool = pool || 5;
let results = [],
together = new Array(pool).fill(null), // 创建工作区,长度为并发数
index = 0; // 标记任务顺序
together = together.map(() => {
// 每个工作区也用promise管理
return new Promise((resolve, reject) => {
// 工作区任务:去整个任务集合中拿任务执行,递归实现
const run = function () {
if (index >= tasks.length) {
resolve();
return;
}
let oldIndex = index, // 保证存储顺序
task = tasks[index++];
task()
.then((result) => {
results[oldIndex] = result;
run();
})
.catch((reason) => {
reject(reason);
});
};
run();
});
});
return Promise.all(together).then(() => results);
}
createRequest(tasks, 2)
.then((results) => {
// 都成功,整体才是成功,按顺序存储结果
console.log(results, "-->成功");
})
.catch((reason) => {
// 只要有一个失败,整体就是失败
console.log(reason, "-->失败");
});
结果:
顺序和
tasks中顺序一致。
解决方案二:
思路:
- 第一种方法是根据并发数创建相同数量的工作区,下面的方法是多工作区,全部拿到,但是不全部执行,先存起来
- 限制多少个就执行多少个,其他不执行,执行完一个再去执行其他,补量执行
function createRequest(tasks, pool, callback) {
if (typeof pool === "function") {
callback = pool;
pool = 5;
}
if (typeof pool !== "number") pool = 5;
if (typeof callback !== "function") callback = function () {};
class TaskQueue {
// 正在运行的个数
running = 0;
queue = [];
results = [];
pushTask(task) {
let self = this;
self.queue.push(task);
self.next();
}
// 发请求
next() {
let self = this;
while (self.running < pool && self.queue.length) {
self.running++;
let task = self.queue.shift(); // 拿任务,没管控顺序
task()
.then((result) => {
self.results.push(result);
})
.finally(() => {
self.running--;
self.next();
});
}
if (self.running === 0) callback(self.results);
}
}
let TQ = new TaskQueue();
tasks.forEach((task) => TQ.pushTask(task));
}
createRequest(tasks, 2, (results) => {
console.log(results);
});
结果:
可以看出顺序不一致。