JavaScript是单线程
因为js语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。
异步任务
但这样很容易造成阻塞,所以把任务分成了同步任务和异步任务两种,异步模式可以同时执行多个异步任务,当主线程遇到异步任务,就把异步任务放进’任务队列’里,执行完同步任务后,再循环的去检查任务队列里面有哪些异步任务要执行,
回调函数
在没有promise前,使用的是回调函数的方式来执行异步任务,就是当主线程开始执行这个异步任务时,就去执行这个异步任务对应的回调函数。
异步任务比如:ajax请求、定时器、事件函数等
回调地狱
但是不同的异步任务耗时不一,我们没有办法控制不同的异步任务的前后顺序,当一个异步任务需要依赖另一个异步任务的结果时,如果不控制这两个异步任务的先后顺序,就很容易会报错:
以向后端发送请求数据为例来说,一个请求b需要在拿到请求a中返回的参数后,才能正确发起请求,这种时候的解决方案是:在请求a的回调函数里去发起b请求,那如果这样的先后依赖多了的话,一层套一层,就会出现回调地狱的问题,代码不易读,耦合度过高,后期亦不好维护。
请求1(function(请求结果1){
请求2(function(请求结果2){
请求3(function(请求结果3){
请求4(function(请求结果4){
请求5(function(请求结果5){
请求6(function(请求结果6){
...
})
})
})
})
})
})
promise
那为了解决回调地狱的问题,ES6就新增了Promise,是异步编程的一种解决方案,有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
promise的2个参数(resolve,reject) 和 3个状态(pending ,fulfilled/resolved,rejected)
Promise是一个构造函数,使用new Promise()得到一个实例,new Promise()需要传进去一个函数,这个函数有2个参数:
一个是resolve回调函数,在异步操作成功时调用,把promise的状态从pending转变到fulfilled(又称resolved),并将异步操作的结果,作为参数传递出去
一个是reject回调函数,在异步操作失败时调用,把Promise的状态从从 pending 变为 rejected,并将异步操作报出的错误,作为参数传递出去
// 创造一个Promise实例
const promise = new Promise((resolve, reject) => {
// ... some code
if (/* 异步操作成功 */){
resolve(value); // value是异步请求成功的结果,resolve把value传出去
} else {
reject(error); // error是异步请求报的错误,reject把error传出去
}
});
Promise.prototype.then()
Promise实例生成后,当promise状态一改变,不管是成功还是失败,就都会来到promise对象的then方法,.then()根据其最终状态,选择特定的状态响应函数执行
then方法可以接受两个回调函数作为参数:
第一个回调函数是promise对象的状态变为resolved的时候调用,
第二个回调函数是promise对象的状态变为rejected时调用。(其中第二个函数是可选的,不一定需要提供)
这两个函数都接受Promise对象传出的值【resolve(value) / reject(error) 】value或者error作为参数,
【then中的函数一定要return一个结果或者一个新的Promise对象(新的异步任务,才可以让之后的then回调接收】
// 创造一个Promise实例
const promise = new Promise((resolve, reject) => {
// ... some code
if (/* 异步操作成功 */){
resolve(value); // value是异步请求成功的结果
} else {
reject(error); // error是异步请求报的错误
}
});
promise.then(
value => { // value是异步请求成功的结果
// 对请求来的结果进行处理,然后返回一个结果或者一个新的Promise对象,才可以让之后的then回调接收
return value;
},
error => { // error是异步请求报的错误
// 对错误进行处理
console.log(error);
}
)
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)因此可以采用链式写法,即then方法后面再调用另一个then方法。
new Promise(请求1)
.then(请求2(请求结果1))
.then(请求3(请求结果2))
.then(请求4(请求结果3))
.then(请求5(请求结果4))
.catch(处理异常(异常信息))
Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)
的别名,如果Promise的状态变为reject时,会被catch捕捉到。
Promise
对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止
一般来说,使用catch
方法代替then()
的第二个参数
所以就把成功的处理逻辑写在.then()里,把失败的处理逻辑写在.cache()里
const sayhello = (order) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(order);
//在异步操作执行完后执行 resolve() 函数
resolve();
}, 1000);
});
};
sayhello("first")
.then(() => {
// 仍然返回一个 Promise 对象
return sayhello("second");
})
.then(() => {
return sayhello("third");
})
.then(() => {
console.log('end');
})
// 处理前面三个Promise产生的错误
.catch((err) => {
console.log(err);
})
Promise.prototype.finally()
finally方法,不管Promise对象最后状态如何,都会执行操作。
Pomise.all()
接受一个数组(迭代对象)作为参数,数组成员都应为Promise
实例
多个 Promise 任务同时执行,如果全部成功执行,则以数组的方式返回所有 Promise 任务的执行结果。
如果有一个 Promise 任务 rejected,则只返回 rejected 任务的结果。
const sayhello = (order) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(order);
resolve('请求成功');
// reject('失败了');
}, 1000);
});
};
const p1 = sayhello('first');
const p2 = sayhello('second');
const p3 = sayhello('third');
Promise.all([p1, p2, p3])
.then((value) => {
console.log(value); // ['请求成功', '请求成功', '请求成功']
})
.catch((err) => {
console.log(err); // 如果注释掉resolve('请求成功'),打开reject('失败了'),则打印:失败了
})
promise.race()
race是赛跑的意思,就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
const f1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功了');
}, 1000);
});
const f2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('失败了');
}, 500);
});
Promise.race([f1, f2])
.then((value) => {
console.log(value);
})
.catch((error) => {
console.log(error); // 打印: '失败了' 因为f2耗时短
})
通过race
可以设置图片请求超时
//请求某个图片资源
function requestImg(){
var p = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){
resolve(img);
}
//img.src = "https://b-gold-cdn.xitu.io/v3/static/img/logo.a7995ad.svg"; 正确的
img.src = "https://b-gold-cdn.xitu.io/v3/static/img/logo.a7995ad.svg1";
});
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise
.race([requestImg(), timeout()])
.then(function(results){
console.log(results);
})
.catch(function(reason){
console.log(reason);
});
async
作为一个关键字放在函数的前面,表示该函数是一个异步函数,async修饰的函数的返回值是一个promise对象
const fun = async () => {
return 'hello';
};
const a = fun();
console.log(a); // Promise { 'hello' }
a.then(result => console.log(result)) // hello
await
即等待,用于等待一个Promise
对象。它只能在异步函数 async function
中使用,否则会报错
它的返回值不是Promise
对象,而是Promise
对象处理之后的结果
await
表达式会暂停当前 async
函数的执行,等待Promise
处理完成,
Promise
正常处理(fulfilled)
,await会拿到resolve
传递出来的异步数据,然后再继续执行 async
函数;Promise
处理异常(rejected)
,await
表达式会把 Promise
的异常原因抛出。// 通过Promise封装Ajax
function http(options) {
// 解构参数
let { method, url, data, headers } = options
// 默认的请求方式
method = method || "GET"
// 默认请求头
headers = headers || {
'Content-type': 'application/json'
};
return new Promise((resolve, reject) => {
let xhr = null;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
xhr.onreadystatechange = () => {
if (xhr.readyState == 4) {
resolve(JSON.parse(xhr.responseText))
}
if (xhr.readyState == 4 && xhr.status == 400) {
reject(JSON.parse(xhr.responseText))
}
}
// 开启
xhr.open(method, url)
// 设置请求头
for (const key in headers) {
// 设置请求参数的类型
xhr.setRequestHeader(key, headers[key]);
}
// 发送请求
xhr.send(JSON.stringify(data));
})
}
// 使用上面封装好的http发送请求
const queryGoods = async () => {
try {
const res = await http({
url: 'http://119.91.125.14:3000/home/goods',
data: {
type: 'pop', // 商品的类型 type pop|new|sell
page: 1 // 页码数
}
})
console.log(res);
return res;
} catch (error) {
console.log(error);
}
}
const res = queryGoods()
console.log(res); // Promise对象