众所周知JavaScript是一种单线程的语言,因此能像Java一样多线程运行,所以促使了JavaScript需要使用异步回调来完成一些逻辑。
JavaScript中最典型的异步就是ajax请求数据以及setTimeout setInterval两个定时方法,而在ES6中正式规范了Promise,主流浏览器现在也大多支持ES6,因此Promise也成了主流的异步解决方案。
Promise是一个构造函数,可以视为Date这样的函数来对待,就像new Date()以后可以得到一个可以操作的对象,当new了一个Promise出来以后,就可以对这个对象进行then和catch的操作,也就是回调后成功跟失败两种情况对应得方法。new Promise对象时候,入参是一个有回调的异步函数,最简单的例如setTimeout()。以下是代码解释
function fun(time, arr = undefined) {
return new Promise((resolve, reject) =>
setTimeout(() => {
try {
console.log(arr[0]);
resolve('success');
} catch (error) {
reject(error);
}
}, time)
);
}
let p1 = fun(1000, [1]),
p2 = fun(2000);
//正确处理
p1.then(data => {
console.log('p1:' + data);
}).catch(data => {
console.warn('p1:' + data);
});
//错误处理
p2.then(data => {
console.log('p2:' + data);
}).catch(data => {
console.warn('p2:' + data);
});
运行结果:
p1与p2分别调用了Promise的then与catch操作,而且也仅仅调用一次,例如将 fun 方法中的setTimeOut改成setInterval,则得到得到以下输出结果:
所以我们可以使用Promise对ajax进行封装,以方便复用:
function getData(data) {
function ajax(resolve, reject) {
$.ajax({
url: 'http://127.0.0.1:8088/url',
type: 'GET',
dataType: 'json',
data: data,
success: data => {
resolve(data);
},
error: () => {
reject('error');
}
});
}
return new Promise(ajax);
}
getData({})
.then(data => {
console.log(data);
})
.catch(data => {
console.warn('错误' + data);
});
基于对Promise的使用与了解,Promise有以下的基本特点:
1)Promise是一个构造函数或者说是一个类
2)Promise的实例化时异步方法是马上执行的
3)Promise的的实例化对象有then与catch两个方法
4)then与catch方法入参为函数,该函数在异步方法回调时候调用then与catch的入参
以下为Promise的简单实现(肯定不严谨
function MyPromise(fn) {
//实例化then与catch方法 覆盖原回调方法为自定义的方法
this.then = thenFn => {
// console.log(this.callFn)
this.callFn.thenFn = thenFn;
return this;
};
this.catch = catchFn => {
this.callFn.catchFn = catchFn;
return this;
};
//原回调方法
this.callFn = {
thenFn: () => {},
catchFn: () => {}
};
//异步函数回调
this.thenFn = data => {
this.callFn.thenFn(data);
};
this.catchFn = data => {
this.callFn.catchFn(data);
};
//执行异步函数
fn(this.thenFn, this.catchFn);
}
使用:
//正确处理
fun(1000, [1])
.then(data => {
console.log('p1:' + data);
})
.catch(data => {
console.warn('p1:' + data);
});
//错误处理
fun(2000)
.then(data => {
console.log('p2:' + data);
})
.catch(data => {
console.warn('p2:' + data);
});
输出:
与原生的Promise基本一致。
前端请求数据如果是同步请求,例如使用 from 标签的action方法就是典型的同步请求,但是同步请求时,浏览器就会停止执行其他操作,等待同步请求返回后,再继续执行其他代码,这种方法适用的情况比较少,因为如果后台请求时间较长,浏览器也只能等待,因此普遍情况下,请求数据都会采用异步请求的方式,这样等待服务器返回数据的时候,浏览器还可以进行其他操作,等服务器做出应答数据返回后,再执行接下来的操作如渲染数据等。
在看到异步请求时,一般想到的就是ajax,ajax是非常典型异步请求,ajax其实就是对XMLHttpRequest进行了封装,因为在jQuery中有ajax的方法,ajax被广泛使用。随着前端技术的进步,MVVM框架的流行,ES6标准的推广,现在很多人开始使用axios 来进行异步请求。
axios的异步其实还是基于XHR的,也是创建XHR进行请求,但是axios是支持Promise的,体积更小,使用起来更符合ES6的规范,而且被尤雨溪推荐了一波,势头很猛。
axios的中文文档:https://www.kancloud.cn/yunye/axios/234845
axios因为支持Promise而被广泛使用,以下是axios的基本用法:
let data = {'code':'00'}
//使用get方法获取数据
axios.get('http://127.0.0.1:8088/url', data).then(response=> {}).catch(error => {});
//或者post方法
data = qs.stringify(data);
axios.get('http://127.0.0.1:8088/url', data).then(response=> {}).catch(error => {});
其实ES6有原生的fetch方法异步请求并且支持Promise,但是因为fetch的一些问题例如无法设置超时时间,无法监听请求进度等问题,fetch无法像axios一样热门。而且axios有丰富的配置,与使用拦截器进行预处理可以解决很多问题,例如权限控制等。
例如添加响应拦截器:
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 预处理
if(response.status === 200){
return Promise.reject(response.data);
}else{
return Promise.reject({'status_code':-1});
}
}, function (error) {
// 错误处理
return Promise.reject(error);
});
Promise使得异步请求更加规范,使用起来更加便捷,使代码逻辑清晰,而axios因为支持Promise得到了广泛的应用风头无量。但是Promise使用起来也是有些复杂,函数套函数,不如普通的异步回调简单粗暴,对初次使用使用者的javaScript基础有一定要求。