让你彻底掌握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 相关文章推荐
另类调用flash无须激活的方法
Dec 27 Javascript
jQuery 使用手册(二)
Sep 23 Javascript
JavaScript读取中文cookie时的乱码问题的解决方法
Oct 14 Javascript
IE6下通过a标签点击切换图片的问题
Nov 14 Javascript
js识别不同浏览器基于userAgent做判断
Jul 29 Javascript
一不小心就做错的JS闭包面试题
Nov 25 Javascript
jQuery实现可拖拽的许愿墙效果【附demo源码下载】
Sep 14 Javascript
AngularJS Toaster使用详解
Feb 24 Javascript
jQuery常见的遍历DOM操作详解
Sep 05 jQuery
动态内存分配导致影响Javascript性能的问题
Dec 18 Javascript
vue 弹窗时 监听手机返回键关闭弹窗功能(页面不跳转)
May 10 Javascript
Js实现粘贴上传图片的原理及示例
Dec 09 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
一篇入门的php Class 文章
2007/04/04 PHP
PHP多文件上传类实例
2015/03/07 PHP
Thinkphp框架 表单自动验证登录注册 ajax自动验证登录注册
2016/12/27 PHP
php提交表单时保留多个空格及换行的文本样式的方法
2017/06/20 PHP
thinkPHP框架实现的短信接口验证码功能示例
2018/06/20 PHP
php依赖注入知识点详解
2019/09/23 PHP
laravel5.0在linux下解决.htaccess无效和去除index.php的问题
2019/10/16 PHP
在laravel中实现ORM模型使用第二个数据库设置
2019/10/24 PHP
JavaScript 关键字屏蔽实现函数
2009/08/02 Javascript
JS实现鼠标滑过链接改变网页背景颜色的方法
2015/10/20 Javascript
完美实现八种js焦点轮播图(上篇)
2016/07/18 Javascript
jQuery自定义图片上传插件实例代码
2017/04/04 jQuery
.net MVC+Bootstrap下使用localResizeIMG上传图片
2017/04/21 Javascript
关于jQuery库冲突的完美解决办法
2017/05/20 jQuery
基于vue2.0实现的级联选择器
2017/06/09 Javascript
详解如何构建Angular项目目录结构
2017/07/13 Javascript
jQuery实现倒计时功能 jQuery实现计时器功能
2017/09/19 jQuery
详解在express站点中使用ejs模板引擎
2017/09/21 Javascript
微信小程序使用radio显示单选项功能【附源码下载】
2017/12/11 Javascript
Vue render深入开发讲解
2018/04/13 Javascript
jQuery中实现text()的方法
2019/04/04 jQuery
JavaScript定时器设置、使用与倒计时案例详解
2019/07/08 Javascript
layui递归实现动态左侧菜单
2019/07/26 Javascript
python构建指数平滑预测模型示例
2019/11/21 Python
python中xlrd模块的使用详解
2021/02/01 Python
Trunki英国官网:儿童坐骑式行李箱
2017/05/30 全球购物
亚历山大·王官网:Alexander Wang
2017/06/23 全球购物
泰国第一的化妆品网站:Konvy
2018/02/25 全球购物
护士自我评价
2014/02/01 职场文书
新闻传播专业求职信
2014/07/22 职场文书
高中生旷课检讨书
2014/10/08 职场文书
Python数组变形的几种实现方法
2022/05/30 Python
NoSQL优缺点与MongoDB数据库简介
2022/06/05 MongoDB
JavaScript前端面试组合函数
2022/06/21 Javascript
CSS 实现角标效果的完整代码
2022/06/28 HTML / CSS
html,css,javascript是怎样变成页面的
2023/05/07 HTML / CSS