Promise,ES里面对异步操作的第一种方案!
学习Promise,让异常操作变得优雅~~~
Promise的精髓在于异步操作的状态管理~
一个Promise最基本用法,他的参数是一个方法,这个方法里有两个参数,一个是异步操作执行成功的回调,一个是失败的回调:
// 第一个参数resolve表示异步操作执行成功时的回调函数
// 第二个参数reject表示执行失败的回调函数
let p = new Promise((resolve,reject) => {
setTimeout(() => {
console.log('Hello~')
if(true) {
resolve() // 执行这个方法时,会输出'成功'
} else {
reject() // 执行这个方法时,会输出'失败'
}
}, 1000);
// then可以有两个函数参数,第一个参数必须
// 第一个参数是异步执行成功时调用
// 第二个参数是异步执行失败时调用
}).then(() => {
console.log('成功');
}, () => {
console.log('失败');
})
传参数的例子:
let p = new Promise((resolve,reject) => {
setTimeout(() => {
console.log('Hello~')
if(true) {
resolve('成功')
} else {
reject('失败')
}
}, 1000);
}).then(res => {
console.log(res);
}, res => {
console.log(res);
})
Promist内部的代码如果不是异步的,也会立刻执行,如:
let p = new Promise((resolve, reject) => {
console.log(1)
})
console.log(2)
-----
1
2
看看下面这个执行顺序,先执行“宏”任务,再执行“微”任务,这个“微”任务是异步操作里的一个概念,then是promise的一个相关任务,
let p = new Promise((resolve, reject) => {
console.log(1)
resolve()
})
console.log(2)
p.then(res => {
console.log(3)
})
------
1
2
3
let p1 = new Promise((resolve, reject) => {
resolve(1);
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 1000);
})
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(3)
}, 1000);
})
console.log(p1)
console.log(p2)
console.log(p3)
setTimeout(() => {
console.log(p2)
}, 2000);
setTimeout(() => {
console.log(p3);
}, 2000);
------------------------------------------------------------------------------------------------------
下面的2,3在输出时,promise不会等待,而是直接执行,所以状态是pendding,4,5是因为等待了2秒,把程序都执行结束了,所以状态是fulfilled以及rejected
1、Promise {: 1}[[Prototype]]: Promise[[PromiseState]]: "fulfilled"[[PromiseResult]]: 1
2、Promise {}[[Prototype]]: Promise[[PromiseState]]: "fulfilled"3[[PromiseResult]]: 2
3、Promise {}[[Prototype]]: Promise[[PromiseState]]: "rejected"[[PromiseResult]]: 3
4、Promise {: 2}[[Prototype]]: Promise[[PromiseState]]: "fulfilled"[[PromiseResult]]: 2
5、Promise {: 3}[[Prototype]]: Promise[[PromiseState]]: "rejected"[[PromiseResult]]: 3
基于上面代码继续完善then():
then里面的失败,可以在then的第二个参数里写失败,也可以直接用.catch()
let p1 = new Promise((resolve, reject) => {
resolve(1);
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 1000);
})
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(3)
}, 1000);
})
p1.then(res => {
console.log(res)
})
p2.then(res => {
console.log(res);
})
p3.catch(err => {
console.log(err);
})
------
1
2
3
Promise的状态是不可逆的:
// 先调用的成功,即便之后再调用失败,也无法改变之前的状态
let p = new Promise((resovle,reject) => {
resovle(1)
reject(2)
})
p.then(res => {
console.log(res);
},err => {
console.log(err)
})
------
1
对之前学的callback hell进行改造: (先来写一个很容易理解的例子,后面再写更炫的例子)
function ajax(url, callback) {
var xmlhttp;
// 1、创建XMLHttpRequest对象
// window.XMLHttpRequest这个对象存在,说明当前浏览器是IE7+或chrome
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
// 兼容早期浏览器
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
// 2、发送请求
xmlhttp.open("GET", url, true);
xmlhttp.send();
// 3、接收服务端响应
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
var obj = JSON.parse(xmlhttp.responseText);
callback(obj)
}
};
}
new Promise((resolve, reject) => {
ajax('http://jsonplaceholder.typicode.com/users', res => {
console.log(res);
resolve()
})
}).then(res => {
console.log('a成功')
return new Promise((resolve, reject) => {
ajax('http://jsonplaceholder.typicode.com/users', res => {
console.log(res);
resolve()
})
})
}).then(res => {
console.log('b成功')
return new Promise((resolve, reject) => {
ajax('http://jsonplaceholder.typicode.com/users', res => {
console.log(res);
resolve()
})
})
}).then(res => {
console.log('c成功');
})
-------------------------------------------------------------------
(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
a成功
(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
b成功
(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
c成功
上面代码中,then().then()属于链式操作,return new Promise的return一定要加,如果不加的话相当于对一个空的promise执行了then(),不加return返回结果是错误的样子:没有按我们想要的顺序执行
------------------------------------------------------------
(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
a成功
b成功
c成功
(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
把上面的代码改造一下,变成更简洁、更炫的:
function ajax(url, callback) {
var xmlhttp;
// 1、创建XMLHttpRequest对象
// window.XMLHttpRequest这个对象存在,说明当前浏览器是IE7+或chrome
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
// 兼容早期浏览器
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
// 2、发送请求
xmlhttp.open("GET", url, true);
xmlhttp.send();
// 3、接收服务端响应
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
var obj = JSON.parse(xmlhttp.responseText);
callback(obj)
}
};
}
function getPromise(url) {
return new Promise((resolve, reject) => {
ajax(url, res => {
resolve(res)
})
})
}
// 链式操作可以直接换行
getPromise('http://jsonplaceholder.typicode.com/users')
.then(res => {
console.log('a成功');
console.log(res)
return getPromise('http://jsonplaceholder.typicode.com/users')
}).then(res => {
console.log('b成功');
console.log(res)
return getPromise('http://jsonplaceholder.typicode.com/users')
}).then(res => {
console.log('c成功');
console.log(res);
})
---------------------------------------------------------------------------
a成功
(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
b成功
(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
c成功
(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
基于上面代码,再把请求失败的操作完善一下:
下面代码的写法注意一下:
// 这种写法的意思是当有参数传递了,再去调用
successCallback && successCallback(obj)
注意看下面程序的注释
function ajax(url, successCallback, failCallback) {
var xmlhttp;
// 1、创建XMLHttpRequest对象
// window.XMLHttpRequest这个对象存在,说明当前浏览器是IE7+或chrome
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
// 兼容早期浏览器
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
// 2、发送请求
xmlhttp.open("GET", url, true);
xmlhttp.send();
// 3、接收服务端响应
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
var obj = JSON.parse(xmlhttp.responseText);
// 这种写法的意思是当有参数传递了,再去调用
successCallback && successCallback(obj)
} else if (xmlhttp.readyState === 4 && xmlhttp.status === 404){
// 这种写法的意思是当有参数传递了,再去调用
failCallback && failCallback(xmlhttp.status)
}
}
}
function getPromise(url) {
return new Promise((resolve, reject) => {
ajax(url, res => { // 成功的情况
resolve(res)
}, err => { // 失败的情况
reject(err)
})
})
}
// 链式操作可以直接换行
getPromise('a.json')
.then(res => {
console.log('a成功');
console.log(res)
return getPromise('http://jsonplaceholder.typicode.com/users')
}, err =>{
console.log(err); // 这里执行后,没有return Promise,所以下面输入undefined
}).then(res => { // 这里能被执行,因为上面没有return Promise,所以默认返回空的Promise
console.log('b成功');
console.log(res)
return getPromise('http://jsonplaceholder.typicode.com/users')
}).then(res => { //
console.log('c成功');
console.log(res);
})
-------------------------------------------------------------
404
b成功
undefined
c成功
(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
如果对失败进行统一的处理:
// 链式操作可以直接换行
getPromise('a.json')
.then(res => {
console.log('a成功');
return getPromise('http://jsonplaceholder.typicode.com/users')
}).then(res => {
console.log('b成功');
return getPromise('http://jsonplaceholder.typicode.com/users')
}).then(res => {
console.log('c成功');
}).catch(err => {
console.log(err);
})
------------------------------------------------------------------------
404 // a.json不存在 ,所以直接进入到最下面的catch里,上面的b,c就不会执行了