ES6语法(3)—— 用promise()对象优雅的解决异步操作
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
上面两段话是阮大神对于promise对象的一些总结,我再浓缩一下
Promise就是用于处理异步操作的!
关于异步操作和同步操作的区别以及他们在浏览器中的表现形式,可以转至这篇文章了解一下
为什么要处理异步操作?
需求1:
在你登录一个网站的时候,这个网站的所有信息都依赖于当前用户的id才能获取,而要得知用户的id,取决于登录接口的返回值。因此,只有当获取用户信息的接口访问成功后,你才能拿到id,才能继续执行别的ajax。(当然实际情况下,id值如此重要的情况下应该保存在session或者cookie中用于前后端交互)
由于异步操作的特殊性,大多数情况下,你还没有拿到用户id,其他的接口请求就带着id为undefined的请求头发出去了,这样就会导致你拿不到其他接口的数据,打开network,可以看到请求头里的user_id都是undefined。
如果要避免这种情况的发生,ajax提供了是否需要对当前接口进行同步操作处理的参数,默认情况下资源请求都是异步的。这是一种方法,当然不可避免地,这个接口会阻塞当前进程,使得其他接口在这个接口状态变成success之前都处于阻塞状态。
ES6提供了更加优雅的解决方案 --- promise()
在解决上述问题之前,先来介绍下promise的一些用法和常识。
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject,这两个参数都是函数,如下所示
new Promise(function(resolve,reject){
异步操作 ajax 定时器等{
if(结果满意)
resolve(value)
else
reject(error)
}
})
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。如下所示
function timeout(ms) {
return new Promise((resolve, reject) => {
if(ms>=)
setTimeout(resolve, ms, 'done'); //setTimeout的第三个参数用于给第一个参数(函数)传参
else
setTimeout(reject, ms, 'error'); //setTimeout的第三个参数用于给第一个参数(函数)传参
});
}
timeout().then((value) => {
console.log(value); //done
},(error)=>{
console.log(error)
});
timeout().then((value) => {
console.log(value);
},(error)=>{
console.log(error) //error
});
promise实例上除了then(),还有catch和finally方法。
catch方法用于指定发生错误时的回调,也就是.then(null,function()),稍微修改下上面的代码
timeout().then((value) => {
console.log(value);
}.catch((error)=>{
console.log(error)
});
当然作者不会那么闲的蛋疼,为了这点小事搞个catch出来,catch还有个功能他可以检测resolve中抛出的异常,也就是说如果resolve中发生异常,JS不会报错,而会把错误抛给catch的第一个参数,如果你写了的话。如下
timeout().then((value) => {
console.log(value);
console.log(somethingUndefined)
}).catch((error)=>{
console.log(error) //somethingUndefined is not defined
});
finally()方法不能传入任何参数,他可以在当前promise状态改变后执行,至于前面的异步操作是否成功执行,跟他无关,也就是说,finally只跟当前异步操作是否执行完毕有关,比如我想在某个异步操作之后,执行一个动画,那么动画跟数据是否传输成功并没有什么关系,不管是成功还是NaA还是undefined,我都可以愉快的执行动画。
简单了解了promise后,大概可以解决需求1中的问题了,来看一场4*100接力赛吧
// 4 * 100 接力赛
let [man1,man2,man3,man4] = [{v:,rank:},{v:,rank:},{v:,rank:},{v:,rank:}]
let runningRace = (man) => {
return new Promise((resolve,reject) => {
// setTimeout的计算单位是毫秒,乘以100效果好一些
setTimeout(resolve,/man['v']*,'rank'+ man['rank'] +'跑了' + /man['v'] + 's')
})
}
runningRace(man1).then((info)=>{
console.log(info)
return runningRace(man2)
}).then((info)=>{
console.log(info)
return runningRace(man3)
}).then((info)=>{
console.log(info)
return runningRace(man4)
}).then((info)=>{
console.log(info)
})
上面这个接力的例子,已经完美的解决了异步操作中的先后执行问题。同时,链式操作的方式使得代码的易读性更强,你无需在回调的嵌套中一步步走入深渊。
需求2:
如果一个接口的访问时间超过400毫秒就算超时,如何解决。
传统的解决思路(在面试的时候想到的),设个定时器,设个全局变量data接受接口要返回的数据,如果400毫秒后,定时器的全局data还未被赋值,说明接口请求超时,当然这种做法不够优雅,如果用promise()呢?
// 接口超时模拟
function ajaxTimeOut(){
return new Promise(function(resolve,reject){
let data = null
// ajax操作用setTimeout模拟
setTimeout(function(){
data = '真实数据来了'
},)
setTimeout(function(){
if(data === null){
reject('接口访问超时')
} else {
resolve(data)
}
},)
})
}
ajaxTimeOut().then(function(data){
console.log('success,data is' + data)
}).catch(function(err){
console.log('err信息:'+err)
})
看起来好像解决了,但是怎么看怎么像愚蠢的解决方案嵌套了一层美丽的皮囊。
promise下还有两个重要的方法没有提到,分别是all()和race()
Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
上面代码中,Promise.all方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。
p的状态由p1、p2、p3决定,分成两种情况。
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
现在我们来改造一下刚才的4*100接力赛,下面进行的是100米的个人赛,我们将在比赛结束后统计结果
Promise.all([runningRace(man1),runningRace(man2),runningRace(man3),runningRace(man4)])
.then(function(results){
console.log(results)
})
可以看到,在比赛结束后(用时最长的异步加载成功后),统计的结果以数组的形式放在第一个参数中。
Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
Promise.race方法的参数与Promise.all方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。
说这么多,反正race的意思就是比赛,就是比哪个异步操作的速度快,改造一下跑步的代码
// 跑步比赛
Promise.race([runningRace(man1),runningRace(man2),runningRace(man3),runningRace(man4)])
.then(function(result){
console.log(result) //谁跑得快输出谁,别的人都忽略
})
上述的结果只有一个,那就是,第一名!
ok,下面来解决下超时的需求,代码如下。
// 接口超时模拟2
// 接口超时模拟2
function ajax(){
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve('success')
},)
})
}
function timeOut(){
return new Promise(function(resolve,reject){
setTimeout(function(){
reject('超时')
},)
})
}
Promise.race([ajax(),timeOut()])
.then(function(data){
console.log(data)
})
.catch(function(err){
console.log(err)
})
ES6语法(3)—— 用promise()对象优雅的解决异步操作的更多相关文章
- ES6系列_14之promise对象的简单使用
1.产生原因 在前端开发中,最常见的的就是"回调",我相信很多人对于这个"回调"可谓是印象深刻呢.究其原因是因为层层回调会造成所谓的“回调地狱 (callbac ...
- ES6 - 基础学习(8): Promise 对象
概述 Promise是异步编程的一种解决方案,比传统的解决方案(多层嵌套回调.回调函数和事件)更强大也更合理.从语法上说,Promise是一个对象,从它可以获取异步操作的消息,Promise 还提供了 ...
- ES6基础知识(Promise 对象)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- es6 语法 (类与对象)
{ // 基本定义和生成实例 class Parent{ constructor(name='mukewang'){ this.name=name; } } let v_parent1=new Par ...
- ES6语法——Promise对象
一.概念 Promise是异步编程的一种解决方案(解决回调地狱的问题),是一个能够获取异步操作信息的对象.Promise的内部保存着某个未来才会结束的事件(通常是一个异步操作) 二.特点 1.Prom ...
- ES6的Promise对象
http://es6.ruanyifeng.com/#docs/promise Promise 对象 Promise 的含义 基本用法 Promise.prototype.then() Promise ...
- ES6的新特性(15)——Promise 对象
Promise 对象 Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大.它由社区最早提出和实现,ES6 将其写进了语言标准,统一了 ...
- ES6深入学习记录(二)promise对象相关
1.Promise的含义 Promise是异步编程的一种解决方案,比传统的解决方案--回调函数和事件更合理和强大.ES6将其写进了语言标准,统一了用法,原生提供了promise对象. 所谓Promis ...
- ES6 Promise 对象
Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理和更强大.它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Pro ...
随机推荐
- koa中间件机制详解
转自:https://cnodejs.org/topic/58fd8ec7523b9d0956dad945 koa是由express原班人马打造的一个更小.更富有表现力.更健壮的web框架. 在我眼中 ...
- 初识go的tomb包
在分析github.com/hpcloud/tail 这个包的源码的时候,发现这个包里用于了一个另外一个包,自己也没有用过,但是这个包在tail这个包里又起来非常大的作用 当时并没有完全弄明白这个包的 ...
- Atitit 创业好处 Atitit 为什么我们要创业
Atitit 创业好处 Atitit 为什么我们要创业 1.1. 提升学历 1 1.2. 提升自己的能力 1 1.3. 拓展视野 站在高层ceo 才能掌握全局.站在产业链高层,才可看到趋势. 1 1. ...
- mongodb应用
一.概述 NoSQL,指的是非关系型的数据库.NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称.NoSQL用于超大规模数据的存储.(例如谷歌或Fa ...
- sfc /scannow命令如何能用虚拟光驱完成修复?(xp下的办法)
我们先光盘文件或用WinRAR压缩软件将ISO文件解压缩到本地磁盘某目录下,如e:\winxp: 在ISO文件上右击,在弹出的菜单中选择“解压到”: 文件较多,久等一会解压完成后文件夹下有很多 ...
- [爬虫]采用Go语言爬取天猫商品页面
最近工作中有一个需求,需要爬取天猫商品的信息,整个需求的过程如下: 修改后端广告交易平台的代码,从阿里上传的素材中解析url,该url格式如下: https://handycam.alicdn.com ...
- surface shader获取像素深度差值
void vert (inout appdata_full v, out Input i) { UNITY_INITIALIZE_OUTPUT(Input, i); i.proj = ComputeS ...
- sql 视图 字段条件统计
) FModelCode,FProductTypeName,FBrandName,FOrganizationName,KOrganizationID,) FALLCount, end) SaleCou ...
- android sdk manager更新地址
参考:http://www.oschina.net/question/1399261_195245 android sdk 用久了,想更新到最新的SDK包: 大连东软信息学院镜像服务器地址:- htt ...
- AI人工智能顶级实战工程师 课程大纲
课程名称 内容 阶段一.人工智能基础 — 高等数学必知必会 1.数据分析 "a. 常数eb. 导数c. 梯度d. Taylore. gini系数f. 信息熵与组合数 ...