让你彻底掌握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 相关文章推荐
Javascript 验证上传图片大小[客户端]
Aug 01 Javascript
js一般方法改写成面向对象方法的无限级折叠菜单示例代码
Jul 04 Javascript
js使用栈来实现10进制转8进制与取除数及余数
Jun 11 Javascript
jQuery实现的支持IE的html滑动条
Mar 16 Javascript
vue v-on监听事件详解
May 17 Javascript
基于Vue单文件组件详解
Sep 15 Javascript
基于Vue制作组织架构树组件
Dec 06 Javascript
Vue从TodoList中学父子组件通信
Feb 05 Javascript
微信小程序如何调用新闻接口实现列表循环
Jul 02 Javascript
axios如何取消重复无用的请求详解
Dec 15 Javascript
Vue关于组件化开发知识点详解
May 13 Javascript
基于angular实现树形二级表格
Oct 16 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解析字符串里所有URL地址的方法
2015/04/03 PHP
Yii rules常用规则示例
2016/03/15 PHP
Ecshop 后台添加新功能栏目及管理权限设置教程
2017/11/21 PHP
TNC vs IO BO3 第一场2.13
2021/03/10 DOTA
javascript的事件描述
2006/09/08 Javascript
js 字符串操作函数
2009/07/25 Javascript
JavaScript Sort 表格排序
2009/10/31 Javascript
js创建对象的几种常用方式小结(推荐)
2010/10/24 Javascript
juqery 学习之五 文档处理 插入
2011/02/11 Javascript
JavaScript之Getters和Setters 平台支持等详细介绍
2012/12/07 Javascript
jQuery通过ajax快速批量提交表单数据
2016/10/25 Javascript
vue监听键盘事件的快捷方法【推荐】
2018/07/11 Javascript
EasyUI 数据表格datagrid列自适应内容宽度的实现
2019/07/18 Javascript
JS通过识别id、value值对checkbox设置选中状态
2020/02/19 Javascript
JavaScript实现手风琴效果
2021/02/18 Javascript
python学习数据结构实例代码
2015/05/11 Python
pygame实现俄罗斯方块游戏
2018/06/26 Python
浅述python中深浅拷贝原理
2018/09/18 Python
使用PyQtGraph绘制精美的股票行情K线图的示例代码
2019/03/14 Python
django框架模板中定义变量(set variable in django template)的方法分析
2019/06/24 Python
python十进制转二进制的详解
2020/02/07 Python
Python实现括号匹配方法详解
2020/02/10 Python
python实现控制台输出彩色字体
2020/04/05 Python
Python爬虫实战案例之爬取喜马拉雅音频数据详解
2020/12/07 Python
adidas美国官网:adidas US
2016/09/21 全球购物
高中生家长会演讲稿
2014/01/14 职场文书
回门宴父母答谢词
2014/01/26 职场文书
法制宣传实施方案
2014/03/13 职场文书
结婚保证书范文
2014/04/29 职场文书
新闻报道策划方案
2014/06/11 职场文书
原料仓仓管员岗位职责
2014/07/08 职场文书
遗失说明具结保证书
2015/02/26 职场文书
捐款仪式主持词
2015/07/04 职场文书
六年级作文之预言作文
2019/10/25 职场文书
CSS 新特性 contain控制页面的重绘与重排问题
2021/04/30 HTML / CSS
服务器SVN搭建图文安装过程
2022/06/21 Servers