让你彻底掌握es6 Promise的八段代码


Posted in Javascript onJuly 26, 2017

前言

新的ES6中引入了promise的概念,目的是让回调更为优雅。层层嵌套的回调会让javascript失去美感和可读性,同时javascript也推荐采用链式的方式去书写函数调用。于是Promise就应运而生。本文将通过八段代码让大家彻底的掌握Promise,下面话不多说,来一起看看详细的介绍:

1.Promise的立即执行性

var p = new Promise(function(resolve, reject){
 console.log("create a promise");
 resolve("success");
});

console.log("after new Promise");

p.then(function(value){
 console.log(value);
});

控制台输出:

"create a promise"
"after new Promise"
"success"

Promise对象表示未来某个将要发生的事件,但在创建(new)Promise时,作为Promise参数传入的函数是会被立即执行的,只是其中执行的代码可以是异步代码。有些同学会认为,当Promise对象调用then方法时,Promise接收的函数才会执行,这是错误的。因此,代码中"create a promise"先于"after new Promise"输出。

2.Promise 三种状态

var p1 = new Promise(function(resolve,reject){
 resolve(1);
});
var p2 = new Promise(function(resolve,reject){
 setTimeout(function(){
 resolve(2); 
 }, 500); 
});
var p3 = new Promise(function(resolve,reject){
 setTimeout(function(){
 reject(3); 
 }, 500); 
});

console.log(p1);
console.log(p2);
console.log(p3);
setTimeout(function(){
 console.log(p2);
}, 1000);
setTimeout(function(){
 console.log(p3);
}, 1000);

p1.then(function(value){
 console.log(value);
});
p2.then(function(value){
 console.log(value);
});
p3.catch(function(err){
 console.log(err);
});

控制台输出:

Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 1}
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
1
2
3
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 2}
Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: 3}

Promise的内部实现是一个状态机。Promise有三种状态:pending,resolved,rejected。当Promise刚创建完成时,处于pending状态;当Promise中的函数参数执行了resolve后,Promise由pending状态变成resolved状态;如果在Promise的函数参数中执行的不是resolve方法,而是reject方法,那么Promise会由pending状态变成rejected状态。

p2、p3刚创建完成时,控制台输出的这两台Promise都处于pending状态,但为什么p1是resolved状态呢? 这是因为p1 的函数参数中执行的是一段同步代码,Promise刚创建完成,resolve方法就已经被调用了,因而紧跟着的输出显示p1是resolved状态。我们通过两个setTimeout函数,延迟1s后再次输出p2、p3的状态,此时p2、p3已经执行完成,状态分别变成resolved和rejected。

3.Promise 状态的不可逆性

var p1 = new Promise(function(resolve, reject){
 resolve("success1");
 resolve("success2");
});

var p2 = new Promise(function(resolve, reject){
 resolve("success");
 reject("reject");
});

p1.then(function(value){
 console.log(value);
});

p2.then(function(value){
 console.log(value);
});

控制台输出:

"success1"
"success"

Promise状态的一旦变成resolved或rejected时,Promise的状态和值就固定下来了,不论你后续再怎么调用resolve或reject方法,都不能改变它的状态和值。因此,p1中resolve("success2")并不能将p1的值更改为success2,p2中reject("reject")也不能将p2的状态由resolved改变为rejected.

4.链式调用

var p = new Promise(function(resolve, reject){
 resolve(1);
});
p.then(function(value){ //第一个then
 console.log(value);
 return value*2;
}).then(function(value){ //第二个then
 console.log(value);
}).then(function(value){ //第三个then
 console.log(value);
 return Promise.resolve('resolve'); 
}).then(function(value){ //第四个then
 console.log(value);
 return Promise.reject('reject');
}).then(function(value){ //第五个then
 console.log('resolve: '+ value);
}, function(err){
 console.log('reject: ' + err);
})

控制台输出:

1
2
undefined
"resolve"
"reject: reject"

Promise对象的then方法返回一个新的Promise对象,因此可以通过链式调用then方法。then方法接收两个函数作为参数,第一个参数是Promise执行成功时的回调,第二个参数是Promise执行失败时的回调。两个函数只会有一个被调用,函数的返回值将被用作创建then返回的Promise对象。这两个参数的返回值可以是以下三种情况中的一种:

  • return 一个同步的值 ,或者 undefined(当没有返回一个有效值时,默认返回undefined),then方法将返回一个resolved状态的Promise对象,Promise对象的值就是这个返回值。
  • return 另一个 Promise,then方法将根据这个Promise的状态和值创建一个新的Promise对象返回。
  • throw 一个同步异常,then方法将返回一个rejected状态的Promise, 值是该异常。

根据以上分析,代码中第一个then会返回一个值为2(1*2),状态为resolved的Promise对象,于是第二个then输出的值是2。第二个then中没有返回值,因此将返回默认的undefined,于是在第三个then中输出undefined。第三个then和第四个then中分别返回一个状态是resolved的Promise和一个状态是rejected的Promise,依次由第四个then中成功的回调函数和第五个then中失败的回调函数处理。

5.Promise then() 回调异步性

var p = new Promise(function(resolve, reject){
 resolve("success");
});

p.then(function(value){
 console.log(value);
});

console.log("which one is called first ?");

控制台输出:

"which one is called first ?"
"success"

Promise接收的函数参数是同步执行的,但then方法中的回调函数执行则是异步的,因此,"success"会在后面输出。

6.Promise 中的异常

var p1 = new Promise( function(resolve,reject){
 foo.bar();
 resolve( 1 ); 
});

p1.then(
 function(value){
 console.log('p1 then value: ' + value);
 },
 function(err){
 console.log('p1 then err: ' + err);
 }
).then(
 function(value){
 console.log('p1 then then value: '+value);
 },
 function(err){
 console.log('p1 then then err: ' + err);
 }
);

var p2 = new Promise(function(resolve,reject){
 resolve( 2 ); 
});

p2.then(
 function(value){
 console.log('p2 then value: ' + value);
 foo.bar();
 }, 
 function(err){
 console.log('p2 then err: ' + err);
 }
).then(
 function(value){
 console.log('p2 then then value: ' + value);
 },
 function(err){
 console.log('p2 then then err: ' + err);
 return 1;
 }
).then(
 function(value){
 console.log('p2 then then then value: ' + value);
 },
 function(err){
 console.log('p2 then then then err: ' + err);
 }
);

控制台输出:

p1 then err: ReferenceError: foo is not defined
p2 then value: 2
p1 then then value: undefined
p2 then then err: ReferenceError: foo is not defined
p2 then then then value: 1

Promise中的异常由then参数中第二个回调函数(Promise执行失败的回调)处理,异常信息将作为Promise的值。异常一旦得到处理,then返回的后续Promise对象将恢复正常,并会被Promise执行成功的回调函数处理。另外,需要注意p1、p2 多级then的回调函数是交替执行的 ,这正是由Promise then回调的异步性决定的。

7.Promise.resolve()

var p1 = Promise.resolve( 1 );
var p2 = Promise.resolve( p1 );
var p3 = new Promise(function(resolve, reject){
 resolve(1);
});
var p4 = new Promise(function(resolve, reject){
 resolve(p1);
});

console.log(p1 === p2); 
console.log(p1 === p3);
console.log(p1 === p4);
console.log(p3 === p4);

p4.then(function(value){
 console.log('p4=' + value);
});

p2.then(function(value){
 console.log('p2=' + value);
})

p1.then(function(value){
 console.log('p1=' + value);
})

控制台输出:

true
false
false
false
p2=1
p1=1
p4=1

Promise.resolve(...)可以接收一个值或者是一个Promise对象作为参数。当参数是普通值时,它返回一个resolved状态的Promise对象,对象的值就是这个参数;当参数是一个Promise对象时,它直接返回这个Promise参数。因此,p1 === p2。但通过new的方式创建的Promise对象都是一个新的对象,因此后面的三个比较结果都是false。另外,为什么p4的then最先调用,但在控制台上是最后输出结果的呢?因为p4的resolve中接收的参数是一个Promise对象p1,resolve会对p1”拆箱“,获取p1的状态和值,但这个过程是异步的,可参考下一节。

8.resolve vs reject

var p1 = new Promise(function(resolve, reject){
 resolve(Promise.resolve('resolve'));
});

var p2 = new Promise(function(resolve, reject){
 resolve(Promise.reject('reject'));
});

var p3 = new Promise(function(resolve, reject){
 reject(Promise.resolve('resolve'));
});

p1.then(
 function fulfilled(value){
 console.log('fulfilled: ' + value);
 }, 
 function rejected(err){
 console.log('rejected: ' + err);
 }
);

p2.then(
 function fulfilled(value){
 console.log('fulfilled: ' + value);
 }, 
 function rejected(err){
 console.log('rejected: ' + err);
 }
);

p3.then(
 function fulfilled(value){
 console.log('fulfilled: ' + value);
 }, 
 function rejected(err){
 console.log('rejected: ' + err);
 }
);

控制台输出:

p3 rejected: [object Promise]
p1 fulfilled: resolve
p2 rejected: reject

Promise回调函数中的第一个参数resolve,会对Promise执行"拆箱"动作。即当resolve的参数是一个Promise对象时,resolve会"拆箱"获取这个Promise对象的状态和值,但这个过程是异步的。p1"拆箱"后,获取到Promise对象的状态是resolved,因此fulfilled回调被执行;p2"拆箱"后,获取到Promise对象的状态是rejected,因此rejected回调被执行。但Promise回调函数中的第二个参数reject不具备”拆箱“的能力,reject的参数会直接传递给then方法中的rejected回调。因此,即使p3 reject接收了一个resolved状态的Promise,then方法中被调用的依然是rejected,并且参数就是reject接收到的Promise对象。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
jQuery $.each的用法说明
Mar 22 Javascript
用js实现的模拟jquery的animate自定义动画(2.5K)
Jul 20 Javascript
仅IE不支持setTimeout/setInterval函数的第三个以上参数
May 25 Javascript
在服务端(Page.Write)调用自定义的JS方法详解
Aug 09 Javascript
详解JavaScript函数绑定
Aug 18 Javascript
javascript获取浏览器类型和版本的方法(js获取浏览器版本)
Mar 13 Javascript
使用javascript实现简单的选项卡切换
Jan 09 Javascript
js指定步长实现单方向匀速运动
Jul 17 Javascript
layui中layer前端组件实现图片显示功能的方法分析
Oct 13 Javascript
layui框架中layer父子页面交互的方法分析
Nov 15 Javascript
jquery实现的放大镜效果示例
Feb 24 jQuery
JS画布动态实现黑客帝国背景效果
Nov 08 Javascript
Bootstrap datepicker日期选择器插件使用详解
Jul 26 #Javascript
js实现图片轮播效果学习笔记
Jul 26 #Javascript
Angular4如何自定义首屏的加载动画详解
Jul 26 #Javascript
vue上传图片组件编写代码
Jul 26 #Javascript
深入讲解xhr(XMLHttpRequest)/jsonp请求之abort
Jul 26 #Javascript
基于ExtJs在页面上window再调用Window的事件处理方法
Jul 26 #Javascript
Angular中自定义Debounce Click指令防止重复点击
Jul 26 #Javascript
You might like
盘点被央视点名过的日本动画电影 一部比一部强
2020/03/08 日漫
PHP4之COOKIE支持详解
2006/10/09 PHP
PHP url 加密解密函数代码
2011/08/26 PHP
javascript CSS画图之基础篇
2009/07/29 Javascript
JavaScript 密码强度判断代码
2009/09/05 Javascript
IE和firefox浏览器的event事件兼容性汇总
2009/12/06 Javascript
js调用后台、后台调用前台等方法总结
2014/04/17 Javascript
JavaScript中获取Radio被选中的值
2015/11/11 Javascript
jquery+ajax实现省市区三级联动效果简单示例
2017/01/04 Javascript
解决bootstrap下拉菜单点击立即隐藏bug的方法
2017/06/13 Javascript
vue中将网页打印成pdf实例代码
2017/06/15 Javascript
Vue调试神器vue-devtools安装方法
2017/12/12 Javascript
JavaScript简单实现关键字文本搜索高亮显示功能示例
2018/07/25 Javascript
解决vue 单文件组件中样式加载问题
2019/04/24 Javascript
echarts实现折线图的拖拽效果
2019/12/19 Javascript
jquery实现图片无缝滚动 蒙版遮蔽效果
2020/01/11 jQuery
selenium 反爬虫之跳过淘宝滑块验证功能的实现代码
2020/08/27 Javascript
[01:02:25]2014 DOTA2华西杯精英邀请赛5 24 NewBee VS VG
2014/05/25 DOTA
Python实现多线程抓取网页功能实例详解
2017/06/08 Python
Python如何处理大数据?3个技巧效率提升攻略(推荐)
2019/04/15 Python
基于Django ORM、一对一、一对多、多对多的全面讲解
2019/07/26 Python
python实现超市商品销售管理系统
2019/10/25 Python
python opencv如何实现图片绘制
2020/01/19 Python
html5的pushstate以及监听浏览器返回事件的实现
2020/08/11 HTML / CSS
amazeui页面分析之登录页面的示例代码
2020/08/25 HTML / CSS
Perfume’s Club美国官网:西班牙第一家在线美容店
2020/06/10 全球购物
专业毕业生个性的自我评价
2013/10/03 职场文书
实习生自我鉴定
2013/12/12 职场文书
新任教师自我鉴定
2014/02/24 职场文书
商业项目策划方案
2014/06/05 职场文书
超市客服工作职责
2014/06/11 职场文书
2014年司机工作总结
2014/11/21 职场文书
初中学生操行评语
2014/12/26 职场文书
HTML+CSS+JS实现图片的瀑布流布局的示例代码
2021/04/22 HTML / CSS
详解JavaScript中Arguments对象用途
2021/08/30 Javascript
html中两种获取标签内的值的方法
2022/06/10 HTML / CSS