结合之前的文章事件循环Event Loop_爬行者c的博客-CSDN博客 先理解JS异步的实现机制。
异步操作结果的处理方式一般会通过回调函数来实现。关于异步编程有以下几种。
Promise
内置类。异步编程,解决回调地域。
参数:一个函数,该函数接收两个函数参数,resolve和reject,且必传
三种状态:pending(进行中,默认状态)、fulfilled/resolved(已成功,异步操作成功后)和rejected(已失败,异步操作失败后)
注:
- 状态一旦修改就不能再改变,一旦执行就无法取消。pending-->resolved/rejected
- then方法的参数期望是函数,传入非函数则会发生值穿透。
- then异步执行,支持链式写法。
let promise = new Promise((resolve, reject) => {
if (true) {
// 将成功参数返回,供then方法使用
resolve("value");
} else {
// 将失败参数返回,供then方法使用
reject("error");
}
});
// then 方法接收两个函数作为参数,第一个参数是 Promise 执行成功时的回调,第二个参数是 Promise 执行失败时的回调,两个函数只会有一个被调用。
promise.then(
value => {
// resolved时调用,value为resolve函数返回的参数
console.log(value);
},
err => {
// rejected时调用,err为reject函数返回的参数
console.log(err);
}
);
promise.catch(error => console.log(error)); //捕获错误
原型上的公有方法
Promise.prototype.then()
Promise.prototype.catch()
Promise.prototype.finally()
new Promise().then(res=>{}).catch(err=>{})
静态方法
Promise.resolve('a')
Promise.reject('a')
Promise.all([p1,p2,p3]]); //所有promise实例都为成功状态 返回值:新的Promise实例
Promise.race([p1,p2,p3]]); //同时发送多个请求,谁先有处理结果就返回,不管是成功还是失败(‘竞速’)
Generator函数
异步。分段执行。yield暂停执行,next恢复执行。缺点是并不能区分什么时候(在第一、第二...阶段)执行。
Generator 有两个区分于普通函数的部分:
-
一是在 function 后面,函数名之前有个 * ;
-
函数内部有 yield 表达式。
function* func(){
console.log("one");
yield '1';
console.log("two");
yield '2';
console.log("three");
yield '3';
}
var f = func();// 返回一个内部指针
// next()方法分阶段执行Generator函数,每次调用完返回一个对象
f.next(); // one {value: '1', done: false}
f.next(); // two {value: '2', done: false}
// return 方法返回给定值,并结束遍历 Generator函数,未给定参数就返回undefined
f.return('over'); // {value: "over", done: true}
f.next(); // {value: undefined, done: true}
async函数
async 是 ES7 才有的与异步操作有关的关键字,和 Promise 和 Generator 有很大关联。
四种使用形式:
- 函数声明: async function foo() {}
- 函数表达式: const foo = async function() {}
- 对象的方式: let obj = { async foo() {} }
- 箭头函数: const foo = async () => {}
返回值:一个 Promise 对象,可以使用 then 方法添加回调函数。
await
用于等待一个 Promise 对象, 它只能在异步函数 async function 内部(async上下文)使用。主要的意图是用来等待 Promise 对象的状态被 resolved, 相当于promise.then。
function testAwait(){
return new Promise((resolve) => {
setTimeout(function(){
console.log("testAwait");
resolve();
}, 1000);
});
}
async function helloAsync(){
await testAwait();
console.log("helloAsync");
}
helloAsync();
// testAwait
// helloAsync
await针对所跟不同表达式的处理方式:
- Promise 对象:await 会暂停执行,等待 Promise 对象 resolve,然后恢复 async 函数的执行并返回解析值。
- 非 Promise 对象:直接返回对应的值,对象/字符串/布尔值/数值/普通函数
Ajax
异步 JavaScript 和 XML。核心技术是XMLHttpRequest(XHR),创建一个Ajax对象要经过:
//1.创建Ajax对象
if(window.XMLHttpRequest){
var xhr=new XMLHttpRequest();
}else{
var xhr=new ActiveXObject("Microsoft.XMLHTTP");
}
//2.连接服务器
xhr.open('GET', url, true); // false为同步,true为异步
//3.发送
xhr.send();
//4.接收
xhr.onreadystatechange=function (){
if(xhr.readyState==4){
if(xhr.status==200){
//alert('成功了:'+xhr.responseText);
}else{
//alert('失败了');
}
}
};
fetch
基于标准 Promise 实现,支持 async/await。脱离了XHR,属于原生JS,是window的一个方法。
相对于XHR发起请求的语法更加简洁。
参数
url 请求地址
options 配置项对象,包括method、headers、body、credentials等参数
get请求
var result = fetch('url', {
credentials: 'include',
headers: {
'Accept': 'application/json, text/plain, */*',
},
});
post请求
var result = fetch('/api/post', {
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/x-www-form-urlencoded'
},
// 注意 post 时候参数的形式
body: "a=1&b=2"
});
处理回调
fetch(url).then(response => {
if (response.ok) {
return response.json() // 解析为可读数据
} else {
return Promise.reject('请求失败')
}
}) .then(data => console.log(data)) //执行结果是 resolve就调用then方法,,第二个then是真正的数据
.catch(e => console.log("error")) //执行结果是 reject就调用catch方法
结合async/await优化写法
async function() {
try {
let response = await fetch(url);
let data = response.json();
console.log(data);
} catch (error) {
console.log(' error');
}
}
注意:
1、fetch只对网络故障或请求被阻止报错,不会拒绝http的错误状态,对404,500都当做成功的请求,将promise标记为resolve。可通过判断Response象中的ok是否为true,如果不是,用 Promise手动添加一个reject。
2、fetch默认不会带cookie,需要添加配置项 credentials: 'include'。
3、fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了量的浪费。
4、fetch没有办法原生监测请求的进度,而XHR可以。
5、兼容性问题(ie完全不支持),针对不同的问题需要引入不同的库。
fetch封装
export default async(url = '', data = {}, type = 'GET', method = 'fetch') => {
type = type.toUpperCase();
url = baseUrl + url;
if (type == 'GET') {
let dataStr = ''; //数据拼接字符串
Object.keys(data).forEach(key => {
dataStr += key + '=' + data[key] + '&';
})
if (dataStr !== '') {
dataStr = dataStr.substr(0, dataStr.lastIndexOf('&'));
url = url + '?' + dataStr;
}
}
if (window.fetch && method == 'fetch') {
let requestConfig = {
credentials: 'include',//为了在当前域名内自动发送 cookie , 必须提供这个选项
method: type,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
mode: "cors",//请求的模式
cache: "force-cache"
}
if (type == 'POST') {
Object.defineProperty(requestConfig, 'body', {
value: JSON.stringify(data)
})
}
try {
const response = await fetch(url, requestConfig);
const responseJson = await response.json();
return responseJson
} catch (error) {
throw new Error(error)
}
} else {
return new Promise((resolve, reject) => {
let requestObj;
if (window.XMLHttpRequest) {
requestObj = new XMLHttpRequest();
} else {
requestObj = new ActiveXObject;
}
let sendData = '';
if (type == 'POST') {
sendData = JSON.stringify(data);
}
requestObj.open(type, url, true);
requestObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
requestObj.send(sendData);
requestObj.onreadystatechange = () => {
if (requestObj.readyState == 4) {
if (requestObj.status == 200) {
let obj = requestObj.response
if (typeof obj !== 'object') {
obj = JSON.parse(obj);
}
resolve(obj)
} else {
reject(requestObj)
}
}
}
})
}
}