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 相关文章推荐
JavaScript CSS菜单功能 改进版
Dec 20 Javascript
JS烟花背景效果实现方法
Mar 03 Javascript
Js获取当前日期时间及格式化代码
Sep 17 Javascript
微信小程序 HTTPS报错整理常见问题及解决方案
Dec 14 Javascript
jQuery Position方法使用和兼容性
Aug 23 jQuery
微信小程序学习笔记之函数定义、页面渲染图文详解
Mar 28 Javascript
Vuex的实战使用详解
Oct 31 Javascript
JS+CSS实现随机点名(实例代码)
Nov 04 Javascript
JS实现横向跑马灯效果代码
Apr 20 Javascript
vue data对象重新赋值无效(未更改)的解决方式
Jul 24 Javascript
JavaScript 如何计算文本的行数的实现
Sep 14 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
php文件包含目录配置open_basedir的使用与性能详解
2017/04/03 PHP
原生js实现改变随意改变div属性style的名称和值的结果
2013/09/26 Javascript
js获取当前路径的简单示例代码
2014/01/08 Javascript
js日期对象兼容性的处理方法
2014/01/28 Javascript
JS获取select的value和text值的简单实例
2014/02/26 Javascript
逐一介绍Jquery data()、Jquery stop()、jquery delay()函数(详)
2015/11/04 Javascript
jQuery获取元素父节点的方法
2016/06/21 Javascript
Javascript 获取鼠标当前的位置实现方法
2016/10/27 Javascript
js实现选项卡内容切换以及折叠和展开效果【推荐】
2017/01/08 Javascript
数组Array的一些方法(总结)
2017/02/17 Javascript
在nginx上部署vue项目(history模式)的方法
2017/12/28 Javascript
vue页面加载闪烁问题的解决方法
2018/03/28 Javascript
JavaScript实现打字游戏
2021/02/19 Javascript
[13:39]2014 DOTA2华西杯精英邀请赛 5 25 NewBee VS DK第一场
2014/05/26 DOTA
[57:53]Secret vs Pain 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
[59:00]OG vs TNC 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
Python 可爱的大小写
2008/09/06 Python
python中sleep函数用法实例分析
2015/04/29 Python
python访问mysql数据库的实现方法(2则示例)
2016/01/06 Python
python实现石头剪刀布小游戏
2021/01/20 Python
python实现AES加密解密
2019/03/28 Python
python 实现从高分辨图像上抠取图像块
2020/01/02 Python
python爬取王者荣耀全皮肤的简单实现代码
2020/01/31 Python
Python 实现将某一列设置为str类型
2020/07/14 Python
从当地商店送来的杂货:Instacart
2018/08/19 全球购物
Jar包的作用是什么
2014/03/30 面试题
SQL数据库笔试题
2016/03/08 面试题
电气自动化大学生求职信
2013/10/16 职场文书
《日月潭》教学反思
2014/02/28 职场文书
汽车专业求职信
2014/06/05 职场文书
Nginx 502 Bad Gateway错误原因及解决方案
2021/03/31 Servers
python 破解加密zip文件的密码
2021/04/22 Python
安装pytorch时报sslerror错误的解决方案
2021/05/17 Python
MySql 8.0及对应驱动包匹配的注意点说明
2021/06/23 MySQL
使用 Docker Compose 构建复杂的多容器App
2022/04/30 Servers