有很多操作都需要从服务器验证是否具有操作权限,为了避免重复代码,为了避免重复代码,我们可以将验证权限的功能当作是一个异步任务,并把它提取成一个方法。
首先来模拟验证权限的过程,更好的理解为什么会有promise的存在。
1 //是否拥有权限 2 function hasPermission(username, callback) { 3 console.log("开始询问服务器," + username + "是否有权限"); 4 //向服务器询问username是否有权限 5 setTimeout(() => { 6 var rad = Math.random(); 7 if (rad < 0.1) { 8 //网络连接失败 9 //后续处理 10 } else if (rad < 0.5) { 11 //后续处理 12 } else { 13 //后续处理 14 } 15 }, 1000) 16 }
按照上述1.0代码编写,所需功能会存在不灵活的情况,可以理解为写死了,而实际中会存在很多种情况。那我们再用伟大的回调来解决这个问题
1 //是否拥有权限 2 function hasPermission(options) { 3 //向服务器询问username是否有权限 4 setTimeout(() => { 5 var rad = Math.random(); 6 if (rad < 0.1) { 7 //网络连接失败 8 options.onEnd(0) 9 } else if (rad < 0.5) { 10 //没有权限 11 options.onEnd(2) 12 } else { 13 //有权限 14 options.onEnd(1) 15 } 16 }, 1000) 17 } 18 19 //评论 20 hasPermission({ 21 username: "张三", 22 onEnd: function(result) { 23 if (result === 0) { 24 //网络有问题:弹出提示 25 } else if (result === 1) { 26 //有权限 27 //评论 28 } else { 29 //没有权限:弹出提示 30 } 31 } 32 })
js 经常会遇到一些异步任务,所谓的异步任务,就是需要经过一段时间或当某一个时机到达后才能得到结果。
例如:
- l 使用Ajax请求服务器,当服务器完成响应后拿到响应结果
- l 监听按钮是否被点击,当按钮被点击后拿到某个文本框的值
- l 使用setTimeout等待一段时间后做某些事情
面对这样的场景,js没有一种标准的模式来进行处理。我们处理这些问题的方式是杂乱的,这就导致了不同的人书写的异步任务代码使用方式不一致。
ES6总结了各种异步场景,并提出一种通用的异步模型。
以前使用node回调模式解决
1.所有的回调函数不能作为属性出现
2所有对调函数必须作为函数的最后参数
3.所有回调函数必须有两个参数,第一个参数表示错误,第二个参数表示结果(erro优先)
if (rad < 0.1) {
//网络连接失败
callback("网络连接失败", null)
} else if (rad < 0.5) {
//没有权限
callback(null, false)
} else {
//有权限
callback(null, true)
}
ES6的异步处理模型
es6将异步场景分为两个阶段和三种状态
两个阶段:unseteled(未决)和seteled(已决)
三种状态:pending(挂起)、resolved(完成)、rejected(失败)
当任务处于未决阶段时,它一定是pending挂起状态,表示任务从开始拿到结果之间的过程。比如:网络完成了各种配置,也发送了请求,但是请求结果还没有拿到。
当任务处于已决阶段时,它只能是resolved和rejected两种状态的一种,表示任务有了一个结果。比如:从服务器拿到了数据(resolved)、网络不好没有拿到数据(rejected)
eg:询问服务器,赵点点是否有权限,服务器告诉你,没有权限。(已决阶段resolved)
任务开始时,始终是未决阶段那任务如何才能走向未决阶段呢?
ES6认为,任务在未决阶段的时候,有能力将其推向已决。比如,当服务器拿到数据后,我们就从未决阶段推向已决的resolved状态,如果网络不好,导致出错了,我们就从未决阶段推向已决的rejected状态
我们把未决推向已决的resolved状态的过程,叫做resolve,从未决推向已决的rejected状态的过程叫做reject
if (rad < 0.1) { // 网络连接失败、服务器报错了 // 推向rejected,附带错误数据 reject("网络连接失败") } else if (rad < 0.5) { // 没有权限 // 推向resolved,附带false(数据) resolve(false) } else { // 有权限 // 推向resolved,附带true(数据) resolve(true) }
这种状态和阶段的变化是不可逆的,也就是说,一旦推向已决,就无法重新改变状态
任务从未决到已决时,可以附带一些数据,比如:跑步完成后的用时、网络请求后拿到的数据
任务已决后(有了结果),可能需要进一步做后续处理,如果任务成功了(resolved),有后续处理,如果任务失败了(rejected),任然可能有后续处理。
我们把针对resolved的后续处理,称之为thenable,针对rejected的后续处理,称之为catchable
promise的基本使用
ES官方制定了一个全新的API来适配上面提到的异步模型,这个API即promise
promise是一个构造函数,通过new Promise()可以创建一个任务对象,构造函数的参数是一个函数,用于处理未决阶段的事务,该函数的执行是立即同步执行的。在函数中,可以通过两个参数自主的在何使的时候将任务推向已决阶段。
拿到promise对象后,可以通过then方法指定后续处理
var task = new Promise((resolve, reject) => { //任务在未决阶段的代码 //立即执行 console.log("开始一百米长跑"); setTimeout(() => { if (Math.random() < 0.5) { //失败:腿摔断了 //推向rejected reject("腿摔断了"); } else { var rad = Math.floor(Math.random() * 100); //跑了多少秒 //推向resolve resolve(rad); } }, 3000); }); task.then(data => { //thenable console.log("跑步完成,用时:" + data) }) task.catch(err => { console.log("出错了,错误原因是:" + err) }) //还可以写成 task.then(data => { //thenable console.log("跑步完成,用时:" + data) },err => { console.log("出错了,错误原因是:" + err) })
so:
promise处理权限验证3.0
//是否拥有权限: 异步任务 function hasPermission(username) { return new Promise((resolve, reject) => { console.log("开始询问服务器," + username + "是否有权限"); //向服务器询问username是否有权限 setTimeout(() => { var rad = Math.random(); if (rad < 0.1) { // 网络连接失败、服务器报错了 // 推向rejected,附带错误数据 reject("网络连接失败") } else if (rad < 0.5) { // 没有权限 // 推向resolved,附带false resolve(false) } else { // 有权限 // 推向resolved,附带true resolve(true) } }, 1000) }) } hasPermission("张三").then(result => { if (result) { console.log("有权限,发布评论") } else { console.log("弹出消息:没有权限") } }, err => { console.log("错误:" + err) })
注意:
1.一旦任务进入已决后,所有企图改变任务状态的代码都将失效
var pro = new Promise((resolve, reject) => { console.log(0) resolve(1); reject(2);//无效 resolve(3) //无效 }) pro.then(result => { console.log(result) }, err => { console.log(err) })
2.以下代码可以让任务达到rejected状态
- 调用reject
- 代码执行报错
- 抛出错误
1 var pro = new Promise((resolve, reject) => { 2 console.log(0) 3 // var obj = null; 4 // obj.method(); //报错 reject(错误对象) 5 //手动抛出错误 throw new Error("abc"); //报错 reject(new Error("abc")) 6 resolve(1); 7 }) 8 9 pro.then(result => { 10 console.log(result) 11 }, err => { 12 console.log(err) 13 })
注意:后续处理函数一定是异步函数,并且放在微队列中
1 例如1: 2 setTimeout(() => { 3 console.log(1) 4 }, 0); 5 var pro = new Promise((resolve, reject) => { 6 console.log(2) 7 resolve(3); //只是简单的把任务推向了resolved,数据为3 8 reject(4); //无效 9 console.log(8) 10 }) 11 //现在,任务已经是resolved 12 pro.then(result => { 13 console.log(result) 14 }, err => { 15 console.log(err) 16 }) 17 console.log(5) 18 19 20 //2 5 3 1 21 22 //再例如2: 23 setTimeout(() => { 24 console.log(1) 25 }, 0); 26 var pro = new Promise((resolve, reject) => { 27 console.log(2) 28 setTimeout(() => { 29 resolve(3) 30 }, 0); 31 console.log(4) 32 }) 33 //现在,任务已经是pending 34 pro.then(result => { 35 console.log(result) 36 }, err => { 37 console.log(err) 38 }) 39 console.log(5) 40 41 //2 4 5 1 3