Promise
目录:
一.promise简介
1) 什么是Promise
2) 解决哪些问题
3) 如何使用
二.promise用法
1) then的链式操作
三.基于promise发送Ajax请求
四.promise基于API
1)catch的用法
五.promise的静态方法
六.promise状态的变化
一. promise简介
什么是Promise:
所谓Promise,简单来说就是一个容器, 里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上来说,Promise是一个对象,它可以获取异步操作的消息; Promise提供统一的API, 各种异步操作都可以用同样的方法进行处理;
Promise是异步编程的一种解决方案;它是一个对象,用于获取异步操作; Promise有三种状态: pending(等待)、fulfilled(成功)、rejected(失败); 状态一旦改变就不会再变, Promise实例创建后,会立即执行;
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
// Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出
能够解决哪些问题:
- 解决回调地狱(异步深层嵌套)问题,代码难以维护,现象: 第一个函数的输出是第二个函数的输入;
2. Promise可以支持多个并发的请求, 获取并发请求中的数据;
3. 这个Promise可以解决异步问题,本身不能说Promise是异步的;
如何使用:
由于Promise是一个构造函数, 所以使用时直接new一个即可:
let p = new Promise((resolve, reject) => {
> // 内部是异步函数
> });
二.Promise基本用法
Promise是一个构造函数, 自身方法: all、reject、resolve等, 原型上有then、catch等方法;
<script type="text/javascript">
/*
1. Promise的基本使用
我们使用new来构建一个Promise, Promise的构造函数接收一个参数,是函数,并且传入两个参数:
resolve, reject, 分别表示异步操作执行成功后的回调函数 和 异步操作执行失败后的回调函数;
*/
var p = new Promise ((resolve, reject) => {
// 2. 这里用于实现异步任务
setTimeout( () => {
var flag = false
if (flag) {
// 3. 正常情况
resolve('hello')
} else {
// 4. 异常情况
reject('出错了')
}
}, 1000)
});
// 打印结果: 报错 '出错了'
// 5. Promise实例生成之后, 可以用then方法指定resolved状态和reject状态的回调函数
// 在then方法中, 也可以直接return数据而不是Promise对象, 在后面的then中就可以接收到数据了;
p.then((data) => {
// then的第一个参数接收函数执行成功的结果
console.log(data)
},(info) => {
// then的第二个参数接收函数执行失败的结果
console.log(info)
})
</script>
Promise的构造函数接收一个参数, 这个参数是一个函数, 函数中需要传入两个参数,它们也是两个函数,由JavaScript 引擎提供,不用自己部署。
resolve: 异步操作执行成功后的回调函数
reject: 异步操作执行失败后的回调函数
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为(resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为
rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数的参数除了正常的值以外,还可能是另一个
Promise 实例,比如像下面这样。
const p1 = new Promise(function (resolve, reject) {
// ...
});
const p2 = new Promise(function (resolve, reject) {
// ...
resolve(p1);
})
/*
p1和p2都是 Promise 的实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作。
注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。
如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;
如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。
*/
进一步说明:
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2
.then(result => console.log(result))
.catch(error => console.log(error))
// Error: fail
/*
上面代码中,p1是一个 Promise,3 秒之后变为rejected。
p2的状态在 1 秒之后改变,resolve方法返回的是p1。
由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。
所以,后面的then语句都变成针对后者(p1)。
又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。
*/
Promise.prototype.then()
Promise实例具有then方法, 也就是说then方法是定义在原型对象 Promise.prototype上的;
它的作用是为Promise实例添加状态改变时的回调函数; then方法的第一个参数是resolved状态的回调函数,
第二个参数(可选)是rejected状态的回调函数;
then方法返回的是一个新的Promise实例(注意: 不是原来那个Promise实例)。因此可以采用链式写法, 即then方法后面再调用另一个then方法。
- then方法可以接收两个参数,第一个对应resolve的回调, 第二个对应reject的回调. 所以我们能够分别拿到它们传递过来的数据;
- then方法中也可以只写一个参数, resolve, 一般是处理正确的结果(这种情况下一般失败的结果由catch来接收);
/*
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function (comments) {
console.log("resolved: ", comments);
}, function (err){
console.log("rejected: ", err);
});
*/
/*
上面代码中,第一个then方法指定的回调函数,返回的是另一个Promise对象。
这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。
如果变为resolved,就调用第一个回调函数,如果状态变为rejected,就调用第二个回调函数
*/
// 如果采用箭头函数,上面的代码可以写得更简洁。
getJSON("/post/1.json").then(
post => getJSON(post.commentURL)
).then(
comments => console.log("resolved: ", comments),
err => console.log("rejected: ", err)
);
三.基于Promise发送Ajax请求
<script type="text/javascript">
// 基于Promise发送Ajax请求
function queryData(url) {
// 1. 创建一个Promise实例
var p = new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if(xhr.readyState != 4) return
if(xhr.readyState == 4 && xhr.status == 200) {
// 2. 处理正常的情况
resolve(xhr.responseText)
} else {
// 3. 处理异常情况
reject('服务器错误')
}
};
xhr.open('get', url)
xhr.send(null)
});
return p;
}
// 注意: 这里需要开启一个服务
// 在Promise中,then主要用于处理函数执行的结果(包括失败的和成功的)
// 在then方法中,可以直接renturn数据而不是Promise对象,在后面的then中就可以接收数据了
queryData('http://localhost:3000/data')
.then((data) => {
console.log(data)
// 4. 想要继续链式编程走下去 需要return
return queryData('http://localhost:3000/data1')
}).then((data) => {
console.log(data)
return queryData('http://localhost:3000/data2')
}).then((data) => {
console.log(data)
});
</script>
四.Promise基于API
.then()
// 得到异步任务正确的结果
.catch()
// 获取异常信息
.finally()
// 成功与否都会执行(不是正式标准)
<script type="text/javascript">
// console.dir(Promise)
function foo() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// resolve(123)
reject('error')
}, 1000)
})
}
// ------------------------------------
// foo()
// .then((data) => {
// // 此时的then处理函数执行成功的结果
// console.log(data)
// })
// .catch((data) => {
// // catch处理函数执行失败的结果
// console.log(data)
// })
// .finally((data) => {
// // 处理函数执行的结果(包括失败结果和成功结果)
// console.log('finished')
// })
// -----------------------------------------------
// 两种写法是等效的
foo()
.then((data) => {
// 得到异步任务正确的结果
console.log(data);
},(data) => {
// 获取异常信息
console.log(data)
})
// 成功与否都会执行
.finally(() => {
console.log('finashed')
})
</script>
Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
- caatch和then接收第二个参数一样, 用来指定reject的回调, 接收执行的错误结果;
- 在执行reslove的回调(then的第一个参数时), 如果抛出异常了, 那么并不会报错卡死, 而是会进入这个catch方法中;
五.Promise的静态方法
.all()
Promise.all方法接受一个数组作为参数, 数组中的对象(p1、p2、p3)均为promise实例(如果不是一个promise, 该项会被用Promise.resolve转换为一个promise); 它的状态由这三个promise实例决定;
.race()
Promise.race方法同样接受一个数组做参数, 当p1、p2、p3中有一个实例的状态发生改变(变为fulfilled或者rejected), p的状态就跟着改变; 并把第一个改变状态的promise的返回值, 传给p的回调函数;
<script type="text/javascript">
/*
Promise常用API-对象方法
*/
// console.dir(Promise)
function queryData(url) {
return Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) return;
if(xhr.readyState == 4 && xhr.status == 200) {
// 处理正常情况
resolve(xhr.responseText);
} else {
// 处理异常情况
reject('服务器错误');
}
}
xhr.open('get', url);
xhr.send(null)
})
var p1 = queryData('http://localhost:3000/a1');
var p2 = queryData('http://localhost:3000/a2');
var p3 = queryData('http://localhost:3000/a3');
Promise.all([p1,p2,p3]).then(function(result) {
// all 中的参数 [p1,p2,p3] 和 返回的结果一 一对应["HELLO TOM", "HELLO JERRY", "HELLO SPIKE"]
console.log(result) //["HELLO TOM", "HELLO JERRY", "HELLO SPIKE"]
})
}
Promise.race([p1,p2,p3]).then((result) => {
// 由于p1执行较快,Promise的then()将获得结果'P1'。p2,p3仍在继续执行,但执行结果将被丢弃;
console.log(result) // "HELLO TOM"
})
</script>
六.Promise状态的变化
-
Promise对象的状态不收外界影响,Promise对象代表一个异步操作, 有三种状态: pending(进行中)、fulfilled(已成功)、rejected(已失败); 只有异步操作的结果可以决定当前是哪一种状态,其他任何操作都无法改变这个状态;
-
一旦状态改变,就不会再变,任何时候都可以得到这个结果;同一时间下只能存在一种状态; Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型);
-
- 初始化状态: pending;
- 当调用resolve(成功),状态: pending => fulfilled;
- 当调用reject(失败), 状态: pending => rejected;
const PNEDING = "pending";
// Promise会一直保持挂起状态, 直到被执行(成功)或者拒绝(失败);
const FULFULLED = "fulfilled";
const REJECTED = "rejected";
class Promise {
const ructor (exector){
let self = this;//缓存当前promise对象
self.status = PENDING;//初始状态,对promise对象调用state(状态)方法,可以查看其状态是“pending"、"resolved"、还是”rejected“
self.value = undefined;// fulfilled状态时 返回的信息
self.reason = undefined;// rejected状态时 拒绝的原因
self.onResolveCallBacks = [];// 存储resolve(成功)状态对应的onFulfilled函数
self.onRejectCallBacks = [];// 存储rejected(失败)状态对应的onRejected函数
let resolve = (value) => {//成功
if(self.status === PENDING){//如果成功则,状态由pending=>fulfilled
self.status = FULFULLED;
self.value = value;
self.onResolveCallBacks.forEach(cb=>cb(self.value));//执行发布
}
}
let reject = (reason) => {//失败
if(self.status === PENDING){//如果失败,则状态由pending=>rejected
self.status = REJECTED;
self.reason = reason; self.onRejectCallBacks.forEach(cb=>cb(self.reason));//执行发布
}
}
try{
exector(resolve,reject)
}catch(e){
reject(e)
}
}
then(onFulfilled,onRejected){
let self=this;
if(self.status === FULFULLED){
onFulfilled(self.value);//成功值
}
if(self.status === REJECTED){
onFulfilled(self.reason);//拒绝原因
}
if(self.status === PENDING){
self.onResolveCallBacks.push(onFulfilled);//订阅发布
self.onRejectCallBacks.push(onRejected);//订阅发布
}
}
/*
promise的决议结果只有两种可能:完成和拒绝,附带一个可选的单个值。
如果Promise完成,那么最终的值称为完成值;
如果拒绝,那么最终的值称为原因。
Promise只能被决议(完成或拒绝)一次。
之后再次试图完成或拒绝的动作都会被忽略;
*/
}
new Promise((resolve,reject)=>{
resolve("成功时调用");
//异步处理
//处理结束后、调用resolve或reject
}).then((data)=>{
console.log(data);//"成功"
},(reason)=>{
console.log(reason);
})