技术概述
? 本节通过传声筒游戏,为初学者讲解Promise对象的属性及常使用方法、执行机制等。在开发中,读取文件、axios都使用了Promise。本节难点是then方法的执行机制。
技术详述
传声筒游戏
? 在《王牌对王牌》节目中有一个“传声筒”游戏,游戏规则是:每队任选5名成员,每个成员用隔板隔开,第一人将对手所出题目,演绎给队友,只能通过音效和动作表达,依次传递给下一位队友,最后一名队友,猜测题目上的信息。猜对得一分,最终积分高的一队获胜。
这个游戏有几个特点:
- 后一个人并不清楚前一个人如何【完成任务】
- 后一个人的【任务】依赖于前一个人完成的情况
回调方式实现
在假设每一轮都会进行的情况下,模拟一下执行滴情况
function playGame() {
next = callback=>{callback()}
next(_=>{
console.log("第一个人传给第二个人")
console.log("歌词‘哈哈哈哈哈‘");
next(_=>{
console.log("第二个人传给第三个人")
console.log("歌词‘哈哈哈哈哈‘");
next(_=>{
console.log("第三个人传给第四个人")
console.log("歌词‘哈哈哈哈哈‘");
next(_=>{
console.log("第四个人传给第五个人")
console.log("歌词‘哈哈哈哈哈‘");
next(_=>{
console.log("第五个人回答")
console.log("歌词‘哈哈哈哈哈‘");
next(_=>{
console.log("得分");
})
})
})
})
})
})
}
这种代码包含了很多的回调,回调套回调,这是在较好情况(保证能执行)下的代码,已经有些复杂,如果代码需求更庞大的话,结果可想而知。下面使用另外一种方式表示:
Promise方式实现
var play = new Promise((resolve,reject)=>{
resolve("传歌词‘呵呵哈哈哈‘")
}).then(value=>{
console.log("第一个人传给第二个人")
console.log("歌词‘哈哈哈哈哈‘");
}).then(value=>{
console.log("第二个人传给第三个人")
console.log("歌词‘哈哈哈哈哈‘");
}).then(value=>{
console.log("第三个人传给第四个人")
console.log("歌词‘哈哈哈哈哈‘");
}).then(value=>{
console.log("第四个人传给第五个人")
console.log("歌词‘哈哈哈哈哈‘");
}).then(value=>{
console.log("第五个人回答")
console.log("歌词‘哈哈哈哈哈‘");
}).then(value=>{
console.log("得分");
})
这可以达到同样的效果,并且这样做的好处是,修改更加容易一些。
Promise
-
是什么?
在MDN上是这样介绍Promise
一个
Promise
对象代表一个在这个 promise 被创建出来时不一定已知的值。它让您能够把异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来。 这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 promise,以便在未来某个时候把值交给使用者。我的理解是,基于上方的例子,Promise是JS中解决异步编程的新方案,本质是一个对象,通过自身属性和方法将操作结果(成功或失败)与相应处理方案关联。
开始了解
- 在游戏开始前,每组需要指定每一轮的成员。
使用Promise表示
var play = new Promise((resolve,reject)=>{
由A给B传递歌词
}).then(
接下来由B给C传递歌词
).then(
接下来由C给D传递歌词
)
在这里,promise调用了then方法,制定下一步的任务。
- 在游戏规则中提到只能通过音效和动作表达,依次传递给下一位队友,【传递】的结果无非歌词传递成功和歌词传递失败
成功将会把歌词完整地传递下去,用Promise表达传递成功
var play = new Promise((resolve,reject)=>{
resolve("传歌词‘呵呵哈哈哈‘")
}).then(value=>{
传递成功,输出传递的内容
})
失败的话有可能是“语言不通”的原因,用Promise表达传递失败
var play = new Promise((resolve,reject)=>{
reject(‘语言不通‘)
}).then(value=>{
},reason=>{
传递失败,输出失败的原因
})
? 在这里,展示了两部分,在promise对象中,如果传递成功,则调用resolve(),传递失败,则调用reject()。
? 在then方法中,接收两个函数作为参数,分别代表传递成功时候执行的函数(onResolved)和传递失败的时候执行的函数(onRejected)
-
举个小例子,由C给D传递歌词时,是一个时间段的任务,在这个过程中,一般有开始、进行中和结束三种状态,结束的结果分为成功和失败
在Promise对象当中,包含状态属性,包含进行中(pending)、成功(resolved)和失败(rejected)三种状态。
在promise对象当中,当调用resolve()时,状态由pending转为resolved;当调用失败时,状态由pending转为rejected
那么,A给B传递歌词可以这么写
var game = new Promise((resolve,reject)=>{
if(歌词传对了){
resolve(‘传歌词‘呵呵哈哈哈‘‘)
}else if(歌词传错了){
reject(‘语言不通‘‘)
}
}).then(value=>{
前方歌词传递成功
},reason=>{
前方歌词传递失败
})
进一步
- 游戏规则中提到,每组5个人参加,并且无论前面歌词传对了还是传错了,都必须进行下去
var game = new Promise((resolve, reject) => {
if (歌词传对了) {
resolve("传歌词‘呵呵哈哈哈 ‘")
} else if (歌词传错了) {
reject("传歌词‘呵呵呵呵 ‘")
}
}).then(value => {
return new Promise((resolve, reject) => {
if (歌词传对了) {
resolve("传歌词‘呵呵哈哈哈 ‘")
} else if (歌词传错了) {
reject("传歌词‘呵呵呵呵 ‘")
}
})
}, reason => {
return new Promise((resolve, reject) => {
reject("传歌词‘呵呵哈哈哈 ‘")
})
}).then(
//同上
)
……
then(value => {
成功,加一分
}, reason => {
失败,不得分
})
在这里,运用到了链式调用,即解决回调地狱的方法,可串联执行多个操作任务。
同时,可以看出,每一次执行then方法会返回一个新的promise对象,由新的promise对象指定下一次then方法中调用的回调函数。
具体描述:
-
Promise.then()方法返回的新promise的结果状态决定下一次then方法执行onRejected还是onResolved
-
返回的状态有三种情况:
① 如果抛出异常(throw err), 状态为rejected, reason为抛出的异常② 如果返回的是非promise的任意值, 状态为resolved, value为返回的值
③ 如果返回的是另一个新promise, 此promise的结果就会成为新promise的结果
下面是一个小例子:
let p = new Promise((resolve, reject) => {
resolve(‘ok‘);
});
//执行 then 方法
let result = p.then(value => {
// console.log(value);
//1. 抛出错误
// throw ‘出了问题‘;
//2. 返回结果是非 Promise 类型的对象
// return 521;
//3. 返回结果是 Promise 对象
// return new Promise((resolve, reject) => {
// // resolve(‘success‘);
// reject(‘error‘);
// });
}, reason => {
console.warn(reason);
});
console.log(result);
再进一步
- Promise的同步和异步
在游戏中,不仅有游戏参与者,还有主持人的角色。为了活跃气氛,主持人在小组玩游戏的时候会同步向观众解释游戏内容
console.log("主持人:游戏开始");
var game = new Promise((resolve, reject) => {
console.log("参与者:歌词是……");
resolve(‘歌词"呵呵哈哈哈 "‘)
})
console.log("主持人:游戏正在进行中");
game.then(value => {
console.log("下一位参与者:接收到"+value);
}, reason => {
})
console.log("主持人:看来这一组非常擅长玩游戏呀");
/**
* 主持人:游戏开始
参与者:歌词是……
主持人:游戏正在进行中
主持人:看来这一组非常擅长玩游戏呀
下一位参与者:接收到歌词"呵呵哈哈哈 "
* /
可以看到 new Promise 实例化过程中执行的代码是同步进行的,而then中注册的回调是异步执行的。
总结
Promise是什么?
- 抽象表达:Promise是JS中进行异步编程的解决方案
- 具体描述:Promise对象是用来封装一个异步操作并可以获取其成功/失败的结果值
Promise通过链式调用解决回调地狱问题
Promise对象包含内置属性和方法
内置属性一:PromiseState,表示状态,有pending、rejected、resolved/fullfiled三种取值
内置属性二:PromiseResult,表示结果值。
方法:
- resolve(),成功时执行,将pending转为resolved/fullfiled
- reject(),失败时执行,将pending转为rejected
- then(),指定回调函数
- onResolved(),成功时执行的回调函数
- onResolved(),失败时执行的回调函数
then方法返回一个新的promise
返回的状态有三种情况:
① 如果抛出异常(throw err), 状态为rejected, reason为抛出的异常
② 如果返回的是非promise的任意值, 状态为resolved, value为返回的值
③ 如果返回的是另一个新promise, 此promise的结果就会成为新promise的结果
Promise的同步和异步:
new Promise 实例化过程中执行的代码是同步进行的
而then中注册的回调是异步执行的