js中的promise是一个异步编程的解决方案,语法层面上他是一个构造函数,名字为Promise()。
它的作用就是将一个任务task封装为一个Promise类的实例对象,这个对象会将任务自动运行并得到任务结果,而且在得到结果的过程中并不会影响到其他任务的进行。由此实现多个任务的并发进行。
实现异步的过程被隐藏在Promise类的实现过程中,我们只需要将任务交给Promise,Promise给我们一个instance,之后通过instance去拿任务结果就可以了。我们可以创建多个Promise类的实例instance。
一:Promise构造函数参数和基本使用介绍
1: Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。resolve和reject是两个函数,由JavaScript引擎提供,不用自己部署。
//resolve, reject名称不能修改
const promise = new Promise(function(resolve, reject) {
// ...some code
if ( /*异步操作成功,执行resolve方法,目的一般是将某些结果返回出去*/ ) {
resolve(value);
} else {
/*异步操作失败,执行reject方法,目的一般也是将某些结果返回出去*/
reject(error);
}
});
2: Promise实例对象的then()方法
Promise 实例生成以后,可以用then方法分别指定resolved状态和rejected 状态的回调函数。也就是对返回的任务结果进行处理。
promise.then(resolved = function(value) {
// success,对返回的结果value进行处理
},
rejected = function(error) {
//failure,直接把错误类型报给用户
});
3:异步加载图片的例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ajax实操</title>
</head>
<body>
<div id="box"></div>
<script>
//resolve, reject名称不能修改
const promise = new Promise(function(resolve, reject) {
// ...some code
if ( /*异步操作成功,执行resolve方法,目的一般是将某些结果返回出去*/ ) {
resolve(value);
} else {
/*异步操作失败,执行reject方法,目的一般也是将某些结果返回出去*/
reject(error);
}
});
let box = document.getElementById('box');
function GETJSON(url) {
/*************************************************************/
function ajaxTask(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("Access-Control-Allow-Origin", "*");
client.send();
}
/*************************************************************/
return new Promise(ajaxTask);
};
/*************************************************************/
promise = GETJSON("https://www.hupu.com/home/v1/news?pageNo=4&pageSize=50");//出现ajax无法跨域的问题,目前还不会解决
promise.then(
function(data) {
console.log(data);;
},
function(error) {
box.innerHTML = '加载失败';
console.log(error);
}
)
</script>
</body>
</html>
4:有顺序的执行某些请求,我们把每个请求(promise形式)串起来搞成链式调用
//验证
function getAuth(){
return new Promise((res,rej)=>{
//关键代码
})
}
//用户信息
function getUsers(){
return new Promise((res,rej)=>{
//关键代码
})
}
//页面信息
function getPageInfo(){
return new Promise((res,rej)=>{
//关键代码
})
}
//请求顺序:先验证 => 再拿用户 => 再拿页面信息
getAuth().then(res => {
if(res.success){
//验证通过,请求用户信息
return getUsers();
}else{
return Promise.reject();
}
}).then(res => {
if(res.success){
//成功获取用户信息,请求页面信息
return getPageInfo();
}else{
return Promise.reject();
}
}).then(res => {
if(res.success){
//成功获取页面信息,反射数据到视图上
//over request
}
})
5:同时发送多个并行的请求,失败的记录,成功的执行相应的逻辑
function requestMain(urls,successfn,failurefn){
//图片url资源路径
let urls = [
'https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=2363246672,1942618513&fm=58',
'https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=4138785756047,2461366902&fm=58',
'https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=378711121878161,3674972757&fm=58',
'https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2412121295993728,2346220807&fm=58',
];
let errorArr = [];//记录错误的信息
//构建promise的函数
function generatePromise(url){
return new Promise((res,rej)=>{
//
})
}
urls.reduce((promise,url,index)=>{
promise.then(res => {
//请求成功执行的回调
successfn(res);
}).catch(err=>{
//失败时,记录失败信息
errorArr.push(err);
}).then(()=>{
if(index === urls.length-1 && errorArr.length > 1){
//全部请求完,并且错误数组不为空时执行回调
failurefn(errorArr);
}else{
promise = generatePromise(url)
}
})
},
Promise.reject(false))
}
6:避免陷入回调地狱
具体使用:
(1) 首先定义三个url的地址;获取第一个url,通过getData(),它return new Promise也就是return一个promise的class的实例。
(2) getData(url1).then 这个then就是new Promise里面的方法,.then(data1 => {console.log(data1) return getData(url2)})。data1就是我们通过url1获取的数据,然后打印出来,这个地方直接return getData(url2),直接return,然后就直接可以再后面.then,我们看data2,然后return getData(url3),.then,最后.catch,它是一个防错处理,万一那一层遇到问题,就会触发error这个回调函数,就会触发catch。
(3) 写法上.then .then .then…… .then都是单层的,是一个管道形式,就是一节对应一节,一节对应一节,它不是一个嵌套形式,不像回调地狱那样,一层一层嵌套。
(4) promise里面还是一个callback的形式,但是它把callback的形式变成了非嵌套的形式,变成了管道式的串联的形式,这就是一个进步,一串一串的形式,串联着走,永远都是一层,这就是promise的一个形式。
二:理论理解
promise是浏览器引擎自带的(但不是所有浏览器都支持promise)
promise的参数是一个函数且必须有这个函数,否则报错,姑且命名为A函数;
let p = new Promise()
console.log(p); // TypeError: Promise resolver undefined is not a function
A函数里有两个参数,这两个参数也是函数。res和rej这两个参数都是函数,名字随意。
res是成功时的回调
rej是失败时的回调
new Promise函数是同步函数,并且是立即执行的
promise共有三种状态,pending,fulfilled,rejected
- 状态只能从pending变为fulfilled状态,成功状态。
- 或者从pending变为rejected状态,失败状态。
例如:pending转变到fulfilled,状态就确定了,只能是fulfilled状态;如果想转成rejected状态,是不能转的。
当promise刚new出来时,是pending状态:
promise函数对象有三个属性,如上图展示的,[[Prototype]]其实就是是__ proto__;其他两个属性如下:
PromiseState:就是promise的现在状态:
当你调用res函数,状态立马变为fulfilled(成功状态)
当你调用rej函数,状态立马变为rejected(失败状态)
如果两个状态都存在,以谁先调用为准:
PromiseResult :res或rej返回的结果
PromiseResult就是res或rej执行后的返回的结果,是返给then的。
当你new 一个promise时,就已经开始自动执行函数。promise是同步的,但then是异步的,要注意区分
promise实例原型上的then函数:
res函数和rej函数返回的数据,谁来接收的,用then函数
then函数里的参数是两个异步回调函数,是异步的哈,第一个参数函数用于接收res返回来的数据,第二个参数函数用于接收rej返回的数据。
then的第一个参数函数里的形参用来接收res返回的数据:
then的第二个参数函数里的形参用来接收rej返回的数据:
其实then里面的函数就是res和rej
如果new promise中没有写res或rej 则then里的函数不会执行:
let p = new Promise((res,rej)=>{
})
p.then(val=>{
console.log(11); // 11不输出
})
then的返回值:
(1) 如果(父级)上一个then返回的是数据或undefined,则(子级)下个then的状态为成功状态,并将父级返回值返回到子级then的参数里面:
let p = new Promise((res,rej)=>{
res(11)
})
// 父级then
let p1 = p.then(val=>{
console.log(val); // 11
// 默认是return undefined
})
// 子级then ,then中的参数是上一个then返回的值,
p1.then(value=>{
console.log(value); // undefined
})
(2) 如果父级then返回的是一个数据:
let p = new Promise((res,rej)=>{
res(11)
})
let p1 = p.then(val=>{
console.log(val);
return {name:'wyy'}
})
p1.then(value=>{
console.log(value);
})
(3) 如果父级then抛出的是一个错误,则父级then的状态为失败状态,并将失败状态的值返回给子级then:
通过throw主动抛出错误或者代码出现错误,则promise的状态为rejected,值为throw的值;或者代码出错也为rejected状态,例如输出一个不存在的a;console.log(a)。
第二个then的promise实例状态是根据p.then的返回值决定的
如果没有return,则默认为undefined,即是fulfilled状态。
catch
catch是处理rej函数的。当返回错误时调用catch:
当new promise出现rej(), throw, 语法错误以上三种形式式,就会调用catch函数。