想必有过项目经验的小伙伴,都对异步请求不陌生。今天小编就带大家来说说什么是异步请求以及如何解决?
一、常见的异步请求方法(数据请求)
1.XMLHttpRequest js异步请求
2.$.ajax jquery异步方法
3.axios (很多公司使用的异步请求库)
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
官方文档:https://www.kancloud.cn/yunye/axios/234845
4.promise是es6里用来解决回调地狱的方案,主要作用是让代码换了个书写形式,由异步形式换成可读性更好的“同步”形式。
5.es7中的async,await同理
6.fetch号称是AJAX的替代品,是在ES6出现的,使用了ES6中的promise对象。Fetch是基于promise设计的。Fetch的代码结构比起ajax简单多了,参数有点像jQuery ajax。但是,一定记住fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。
二、解决异步请求的发展及优缺点
setTimeout(() => {
console.log('settimeout');
}, 1000);
console.log(1);
$.ajax({
url:'',
success(){
$.ajax({
url:'',
success(){
$.ajax({
url:'..'
})
}
})
}
})
试想,如果再多几个异步函数,代码整体的维护性,可读性都变的极差,如果出了bug,修复的排查过程也变的极为困难,这个便是所谓的 回调函数地狱。
优点: 解决了同步问题(解决了 只要有一个任务耗时间很长,后面的任务必须排队进行执行,会延迟严重。)
缺点:回调地狱,不能用 try catch 捕获错误,不能return
Promise是解决异步回调的ES语法的标准,通常用来解决异步嵌套和多异步同时完成回调等问题。我的理解就是:在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了。
Promise是一个构造函数,相当于一个容器,把异步代码包裹在里面,promise有三个状态(pending(进行中)、fulfilled(已成功)和rejected(已失败)),初始化为pending,当异步请求成功后调用resolve函数,状态从pending—>fulfilled,失败的时候调用reject,状态从pending—>rejected。状态不可逆。
Promise的几种方法:
(1)、Promise.prototype.then()
then方法是定义在原型对象Promise.prototype上的,他的作用是为Promise实例添加状态 改变是的回调函数。
then方法的第一个参数是Resolved状态的回调函数,第二个参数(可选)是Rejected状态的回调函数。为宏任务中的微任务。
(2)、Promise.prototype.catch()
此方法是.then(null,rejection)的别名,用于指定错误发生时的回调函数。
(3)、Promise.all()
此方法用于将多个Promise实例包装成一个新的promise实例。
(4)、Promise.race()
也是将多个Promise实例包装成一个新的promise实例。
(5)、Promise.resolve()
将现有对象转换为Promise对象
(6)、Promise.reject()
Promise.reject(reason)方法返回一个状态为Rejected的新Promise实例。
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。
下面是promise的基本用法:
const promise = new Promise(function (resolve, reject) {
// ... some code
if (/* 异步操作成功 */) {
resolve(value);
} else {
reject(error);
}
}).then(function (value) {
// success
}, function (error) {
// failure
});
简单案例1:
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
// resolve('done')
});
}
timeout(100).then((value) => {
console.log(value); //done
});
上面代码中,timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果。过了指定的时间(ms参数)以后,Promise实例的状态变为resolved,就会触发then方法绑定的回调函数。
简单案例2:
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数, 将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。
简单案例3:
function loadImageAsync(url) {
return new Promise(function (resolve, reject) {
const image = new Image();
image.onload = function () {
resolve(image);
};
image.onerror = function () {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
上面代码中,使用Promise包装了一个图片加载的异步操作。如果加载成功,就调用resolve方法,否则就调用reject方法。
简单案例4.
下面是一个用Promise对象实现的 Ajax 操作的例子。
const getJSON = function (url) {
const promise = new Promise(function (resolve, reject) {
const handler = function () {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function (json) {
console.log('Contents: ' + json);
}, function (error) {
console.error('出错了', error);
});
上面代码中,getJSON是对 XMLHttpRequest 对象的封装,用于发出一个针对 JSON 数据的 HTTP 请求,并且返回一个Promise对象。需要注意的是,在getJSON内部,resolve函数和reject函数调用时,都带有参数。
Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
优点: 解决回调地狱
缺点: 无法取消promise ,错误需要通过回调函数获取 ,书写麻烦,不能实现异步代码,同步执行的需求(配合async函数使用即可)
可详细学习: https://es6.ruanyifeng.com/#docs/promise
3、 generator
特点:可以控制函数的执行,可以配合 co 函数库使用
4、 async/await
async/await 是异步的终极解决方案。
要注意的是,await只能在函数中用async关键字标记后才能使用
1、async 表示这是一个async函数,await只能用在这个函数里面。
2、await 表示在这里等待promise返回结果了,再继续执行。
3、await 后面跟着的应该是一个promise对象(当然,其他返回值也没关系,只是会立即执行,不过那样就没有意义了…)
Async函数表示函数里面可能会有异步方法,并且函数返回的是promise对象。遇到await会立即执行await表达式,然后再将其后的代码放入到微任务中,让出执行站让同步代码先执行。
下面再带领大家看一些面试中常出现的异步代码题:
console.log(0)
new Promise(function(res,rej){
console.log(1);
// res()
// rej()
}).then(function(){
console.log('then');
}).catch(function(){
console.log('catch');
})
console.log(3);
2.async/await
async function show(){
console.log(1);
await dev() // await 后面的代码为异步微任务代码
console.log(2);
}
function dev(){
console.log('await');
}
console.log('first');
show()
3.综合练习
async function async1(){
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2(){
console.log('async2');
}
console.log('script start');
setTimeout(function(){
console.log('settimeout');
},0)
async1();
new Promise(function(resolve){
console.log('promise1');
resolve()
}).then(function(){
console.log('promise2');
})
console.log('script end');
//打印:script start -> async1 start -> async2 -> promise1 -> script end -> async1 end -> promise2 -> settimeout
文章最后,感谢大家的观看,祝大家学有所成!