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 相关文章推荐
Add Formatted Data to a Spreadsheet
Jun 12 Javascript
用JQuery调用Session的实现代码
Oct 29 Javascript
两个Javascript小tip资料
Nov 23 Javascript
js移除事件 js绑定事件实例应用
Nov 28 Javascript
JS实现弹性漂浮效果的广告代码
Sep 02 Javascript
一不小心就做错的JS闭包面试题
Nov 25 Javascript
AngularJS $http模块POST请求实现
Apr 08 Javascript
JavaScript树的深度优先遍历和广度优先遍历算法示例
Jul 30 Javascript
小程序根据手机机型设置自定义底部导航距离
Jun 04 Javascript
微信小程序开发搜索功能实现(前端+后端+数据库)
Mar 04 Javascript
使用konva和vue-konva库实现拖拽滑块验证功能
Apr 27 Javascript
JS事件循环机制event loop宏任务微任务原理解析
Aug 04 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
《超神学院》霸气归来, 天使彦上演维多利亚的秘密
2020/03/02 国漫
Discuz!5的PHP代码高亮显示插件(黑暗中的舞者更新)
2007/01/29 PHP
PHP导入Excel到MySQL的方法
2011/04/23 PHP
PHP利用hash冲突漏洞进行DDoS攻击的方法分析
2015/03/26 PHP
基于jquery的一个图片hover的插件
2010/04/24 Javascript
Javascript判断对象是否相等实现代码
2013/03/18 Javascript
IE下双击checkbox反应延迟问题的解决方法
2014/03/27 Javascript
jQuery中slice()方法用法实例
2015/01/07 Javascript
JS使用parseInt解析数字实现求和的方法
2015/08/05 Javascript
Bootstrap的modal拖动效果
2016/12/25 Javascript
JS验证码实现代码
2017/09/14 Javascript
Vue2.0 给Tab标签页和页面切换过渡添加样式的方法
2018/03/13 Javascript
vue多次循环操作示例
2019/02/08 Javascript
基于layui table返回的值的多级嵌套的解决方法
2019/09/19 Javascript
vue设置导航栏、侧边栏为公共页面的例子
2019/11/01 Javascript
Layui表格监听行单双击事件讲解
2019/11/14 Javascript
解决echarts vue数据更新,视图不更新问题(echarts嵌在vue弹框中)
2020/07/20 Javascript
vue实现简单计算商品价格
2020/09/14 Javascript
Vue 防止短时间内连续点击后多次触发请求的操作
2020/11/11 Javascript
[55:25]2018DOTA2亚洲邀请赛3月29日 小组赛A组 VG VS OG
2018/03/30 DOTA
Python去除列表中重复元素的方法
2015/03/20 Python
Python中的数学运算操作符使用进阶
2016/06/20 Python
python 中的list和array的不同之处及转换问题
2018/03/13 Python
Python zip()函数用法实例分析
2018/03/17 Python
分分钟入门python语言
2018/03/20 Python
Python学习笔记之Break和Continue用法分析
2019/08/14 Python
如何实现在jupyter notebook中播放视频(不停地展示图片)
2020/04/23 Python
python根据用户需求输入想爬取的内容及页数爬取图片方法详解
2020/08/03 Python
HTML5 placeholder属性详解
2016/06/22 HTML / CSS
实习医生自我评价
2013/09/22 职场文书
简历中个人自我评价范文
2013/12/26 职场文书
2015年元旦文艺晚会总结(学院)
2014/11/28 职场文书
优秀团员自我评价
2015/03/10 职场文书
MySQL 隔离数据列和前缀索引的使用总结
2021/05/14 MySQL
Python开发工具Pycharm的安装以及使用步骤总结
2021/06/24 Python
详解Python中的for循环
2022/04/30 Python