ES6关于Promise的用法详解


Posted in Javascript onMay 07, 2018

Node的产生,大大推动了Javascript这门语言在服务端的发展,使得前端人员可以以很低的门槛转向后端开发。

当然,这并不代表迸发成了全栈。全栈的技能很集中,绝不仅仅是前端会写一些HTML和一些交互,后台熟悉数据库的增删查改。

想必接触过Node的人都知道,Node是以异步(Async)回调著称的,其异步性提高了程序的执行效率,但同时也减少了程序的可读性。如果我们有几个异步操作,并且后一个操作需要前一个操作返回的数据才能执行,这样按照Node的一般执行规律,要实现有序的异步操作,通常是一层加一层嵌套下去。

为了解决这个问题,ES6提出了Promise的实现。

含义

Promise 对象用于一个异步操作的最终完成(或失败)及其结果值的表示。简单点说,它就是用于处理异步操作的,异步处理成功了就执行成功的操作,异步处理失败了就捕获错误或者停止后续操作。

它的一般表示形式为:

new Promise(
  /* executor */
  function(resolve, reject) {
    if (/* success */) {
      // ...执行代码
      resolve();
    } else { /* fail */
      // ...执行代码
      reject();
    }
  }
);

其中,Promise中的参数executor是一个执行器函数,它有两个参数resolve和reject。它内部通常有一些异步操作,如果异步操作成功,则可以调用resolve()来将该实例的状态置为fulfilled,即已完成的,如果一旦失败,可以调用reject()来将该实例的状态置为rejected,即失败的。

我们可以把Promise对象看成是一条工厂的流水线,对于流水线来说,从它的工作职能上看,它只有三种状态,一个是初始状态(刚开机的时候),一个是加工产品成功,一个是加工产品失败(出现了某些故障)。同样对于Promise对象来说,它也有三种状态:

  1. pending:初始状态,也称为未定状态,就是初始化Promise时,调用executor执行器函数后的状态。
  2. fulfilled:完成状态,意味着异步操作成功。
  3. rejected:失败状态,意味着异步操作失败。

它只有两种状态可以转化,即

  1. 操作成功:pending -> fulfilled
  2. 操作失败:pending -> rejected

并且这个状态转化是单向的,不可逆转,已经确定的状态(fulfilled/rejected)无法转回初始状态(pending)。

方法

Promise.prototype.then()

Promise对象含有then方法,then()调用后返回一个Promise对象,意味着实例化后的Promise对象可以进行链式调用,而且这个then()方法可以接收两个函数,一个是处理成功后的函数,一个是处理错误结果的函数。

如下:

var promise1 = new Promise(function(resolve, reject) {
 // 2秒后置为接收状态
 setTimeout(function() {
  resolve('success');
 }, 2000);
});

promise1.then(function(data) {
 console.log(data); // success
}, function(err) {
 console.log(err); // 不执行
}).then(function(data) {
 // 上一步的then()方法没有返回值
 console.log('链式调用:' + data); // 链式调用:undefined 
}).then(function(data) {
 // ....
});

在这里我们主要关注promise1.then()方法调用后返回的Promise对象的状态,是pending还是fulfilled,或者是rejected?

返回的这个Promise对象的状态主要是根据promise1.then()方法返回的值,大致分为以下几种情况:

  1. 如果then()方法中返回了一个参数值,那么返回的Promise将会变成接收状态。
  2. 如果then()方法中抛出了一个异常,那么返回的Promise将会变成拒绝状态。
  3. 如果then()方法调用resolve()方法,那么返回的Promise将会变成接收状态。
  4. 如果then()方法调用reject()方法,那么返回的Promise将会变成拒绝状态。
  5. 如果then()方法返回了一个未知状态(pending)的Promise新实例,那么返回的新Promise就是未知状态。
  6. 如果then()方法没有明确指定的resolve(data)/reject(data)/return data时,那么返回的新Promise就是接收状态,可以一层一层地往下传递。

转换实例如下:

var promise2 = new Promise(function(resolve, reject) {
 // 2秒后置为接收状态
 setTimeout(function() {
  resolve('success');
 }, 2000);
});

promise2
 .then(function(data) {
  // 上一个then()调用了resolve,置为fulfilled态
  console.log('第一个then');
  console.log(data);
  return '2';
 })
 .then(function(data) {
  // 此时这里的状态也是fulfilled, 因为上一步返回了2
  console.log('第二个then');
  console.log(data); // 2

  return new Promise(function(resolve, reject) {
   reject('把状态置为rejected error'); // 返回一个rejected的Promise实例
  });
 }, function(err) {
  // error
 })
 .then(function(data) {
  /* 这里不运行 */
  console.log('第三个then');
  console.log(data);
  // ....
 }, function(err) {
  // error回调
  // 此时这里的状态也是fulfilled, 因为上一步使用了reject()来返回值
  console.log('出错:' + err); // 出错:把状态置为rejected error
 })
 .then(function(data) {
  // 没有明确指定返回值,默认返回fulfilled
  console.log('这里是fulfilled态');
});

Promise.prototype.catch()

catch()方法和then()方法一样,都会返回一个新的Promise对象,它主要用于捕获异步操作时出现的异常。因此,我们通常省略then()方法的第二个参数,把错误处理控制权转交给其后面的catch()函数,如下:

var promise3 = new Promise(function(resolve, reject) {
 setTimeout(function() {
  reject('reject');
 }, 2000);
});

promise3.then(function(data) {
 console.log('这里是fulfilled状态'); // 这里不会触发
 // ...
}).catch(function(err) {
 // 最后的catch()方法可以捕获在这一条Promise链上的异常
 console.log('出错:' + err); // 出错:reject
});

Promise.all()

Promise.all()接收一个参数,它必须是可以迭代的,比如数组。

它通常用来处理一些并发的异步操作,即它们的结果互不干扰,但是又需要异步执行。它最终只有两种状态:成功或者失败。

它的状态受参数内各个值的状态影响,即里面状态全部为fulfilled时,它才会变成fulfilled,否则变成rejected。

成功调用后返回一个数组,数组的值是有序的,即按照传入参数的数组的值操作后返回的结果。如下:

// 置为fulfilled状态的情况
var arr = [1, 2, 3];
var promises = arr.map(function(e) {
 return new Promise(function(resolve, reject) {
  resolve(e * 5);
 });
});

Promise.all(promises).then(function(data) {
  // 有序输出
 console.log(data); // [5, 10, 15]
 console.log(arr); // [1, 2, 3]
});
// 置为rejected状态的情况
var arr = [1, 2, 3];
var promises2 = arr.map(function(e) {
 return new Promise(function(resolve, reject) {
  if (e === 3) {
   reject('rejected');
  }
  resolve(e * 5);
 });
});

Promise.all(promises2).then(function(data) {
 // 这里不会执行
 console.log(data);
 console.log(arr);
}).catch(function(err) {
 console.log(err); // rejected
});

Promise.race()

Promise.race()和Promise.all()类似,都接收一个可以迭代的参数,但是不同之处是Promise.race()的状态变化不是全部受参数内的状态影响,一旦参数内有一个值的状态发生的改变,那么该Promise的状态就是改变的状态。就跟race单词的字面意思一样,谁跑的快谁赢。如下:

var p1 = new Promise(function(resolve, reject) {
 setTimeout(resolve, 300, 'p1 doned');
});

var p2 = new Promise(function(resolve, reject) {
 setTimeout(resolve, 50, 'p2 doned');
});

var p3 = new Promise(function(resolve, reject) {
 setTimeout(reject, 100, 'p3 rejected');
});

Promise.race([p1, p2, p3]).then(function(data) {
 // 显然p2更快,所以状态变成了fulfilled
 // 如果p3更快,那么状态就会变成rejected
 console.log(data); // p2 doned
}).catch(function(err) {
 console.log(err); // 不执行
});

Promise.resolve()

Promise.resolve()接受一个参数值,可以是普通的值,具有then()方法的对象和Promise实例。正常情况下,它返回一个Promise对象,状态为fulfilled。但是,当解析时发生错误时,返回的Promise对象将会置为rejected态。如下:

// 参数为普通值
var p4 = Promise.resolve(5);
p4.then(function(data) {
 console.log(data); // 5
});

// 参数为含有then()方法的对象
var obj = {
 then: function() {
  console.log('obj 里面的then()方法');
 }
};

var p5 = Promise.resolve(obj);
p5.then(function(data) {
 // 这里的值时obj方法里面返回的值
 console.log(data); // obj 里面的then()方法
});

// 参数为Promise实例
var p6 = Promise.resolve(7);
var p7 = Promise.resolve(p6);
p7.then(function(data) {
 // 这里的值时Promise实例返回的值
 console.log(data); // 7
});

// 参数为Promise实例,但参数是rejected态
var p8 = Promise.reject(8);
var p9 = Promise.resolve(p8);
p9.then(function(data) {
 // 这里的值时Promise实例返回的值
 console.log('fulfilled:'+ data); // 不执行
}).catch(function(err) {
 console.log('rejected:' + err); // rejected: 8
});

Promise.reject()

Promise.reject()和Promise.resolve()正好相反,它接收一个参数值reason,即发生异常的原因。此时返回的Promise对象将会置为rejected态。如下:

var p10 = Promise.reject('手动拒绝');
p10.then(function(data) {
 console.log(data); // 这里不会执行,因为是rejected态
}).catch(function(err) {
 console.log(err); // 手动拒绝
}).then(function(data) {
 // 不受上一级影响
 console.log('状态:fulfilled'); // 状态:fulfilled
});

总之,除非Promise.then()方法内部抛出异常或者是明确置为rejected态,否则它返回的Promise的状态都是fulfilled态,即完成态,并且它的状态不受它的上一级的状态的影响。

总结

大概常用的方法就写那么多,剩下的看自己实际需要再去了解。

解决Node回调地狱的不止有Promise,还有Generator和ES7提出的Async实现。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js玩一玩WSH吧
Feb 23 Javascript
jQuery select的操作实现代码
May 06 Javascript
javascript 面向对象的JavaScript类
May 04 Javascript
原生JS绑定滑轮滚动事件兼容常见浏览器
Jun 30 Javascript
提高jQuery性能优化的技巧
Aug 03 Javascript
跟我学习javascript的Date对象
Nov 19 Javascript
Node.js操作mysql数据库增删改查
Mar 30 Javascript
js仿手机页面文件下拉刷新效果
Oct 14 Javascript
vuex实现简易计数器
Oct 27 Javascript
基于Vuejs和Element的注册插件的编写方法
Jul 03 Javascript
react-router4按需加载(踩坑填坑)
Jan 06 Javascript
使用vue脚手架(vue-cli)搭建一个项目详解
May 09 Javascript
React Form组件的实现封装杂谈
May 07 #Javascript
vue如何将v-for中的表格导出来
May 07 #Javascript
浅谈Vue 数据响应式原理
May 07 #Javascript
浅谈Vue响应式(数组变异方法)
May 07 #Javascript
在HTML文档中嵌入JavaScript的四种方法
May 07 #Javascript
详解JavaScript的BUG和错误
May 07 #Javascript
vue实现个人信息查看和密码修改功能
May 06 #Javascript
You might like
一个分页的论坛
2006/10/09 PHP
php数据库密码的找回的步骤
2011/01/12 PHP
邮箱正则表达式实现代码(针对php)
2013/06/21 PHP
PHP函数in_array()使用详解
2014/08/20 PHP
PHP使用strtotime获取上个月、下个月、本月的日期
2015/12/30 PHP
php中输出json对象的值(实现方法)
2018/03/07 PHP
Javascript 类、命名空间、代码组织代码
2011/07/31 Javascript
javaScript复制功能调用实现方案
2012/12/13 Javascript
js对象转json数组的简单实现案例
2014/02/28 Javascript
实现checkbox全选、反选、取消JavaScript小脚本异常
2014/04/10 Javascript
14个有用的Jquery技巧分享
2015/01/08 Javascript
jquery实现表单输入时提示文字滑动向上效果
2015/08/10 Javascript
Angularjs全局变量被作用域监听的正确姿势
2016/02/06 Javascript
Node.js编写爬虫的基本思路及抓取百度图片的实例分享
2016/03/12 Javascript
jQuery File Upload文件上传插件使用详解
2016/12/06 Javascript
深入理解JavaScript中的for循环
2017/02/07 Javascript
Ionic3 UI组件之Gallery Modal详解
2017/06/07 Javascript
Angular实现的内置过滤器orderBy排序与模糊查询功能示例
2017/12/29 Javascript
小程序scroll-view组件实现滚动的示例代码
2018/09/20 Javascript
angular将html代码输出为内容的实例
2018/09/30 Javascript
DatePickerDialog 自定义样式及使用全解
2019/07/09 Javascript
Python随机函数random()使用方法小结
2018/04/29 Python
python中itertools模块zip_longest函数详解
2018/06/12 Python
python3爬虫之设计签名小程序
2018/06/19 Python
Pandas透视表(pivot_table)详解
2019/07/22 Python
Python3 把一个列表按指定数目分成多个列表的方式
2019/12/25 Python
快速解决jupyter notebook启动需要密码的问题
2020/04/21 Python
CSS3 实现时间轴动画
2020/11/25 HTML / CSS
HTML5 拖拽批量上传文件的示例代码
2018/03/28 HTML / CSS
美国首屈一指的礼品篮供应商:GiftTree
2018/01/06 全球购物
英国设计师珠宝网站:Joshua James Jewellery
2020/03/01 全球购物
error和exception有什么区别
2012/10/02 面试题
《中国梦我的梦》中学生演讲稿
2014/08/20 职场文书
标准离婚协议书(2014版)
2014/10/05 职场文书
教师节祝酒词
2015/08/11 职场文书
提取视频中的音频 Python只需要三行代码!
2021/05/10 Python