ES6中的Promise

一、Promise

Promise是一个ES6提出一个新语法,用来优化异步代码的写法。在没有它之前,javascript中的异步处理,大多是利用回调函数来实现的。

1、构造器

var p1 = new Promise(function(resolve,reject){
  // 执行异步代码
  // 调用resolve,或者reject
});
console.dir(p1)

要点:

  • 构造器必须要给定一个参数,如果不给就是会报错。例如,new Promise() 报错的信息是: Promise resolver undefined is not a function

  • 构造器的实参是一个函数,这个函数的特殊之处在于它有两个形参(resolve,reject),这两个形参也是函数。在格式上,也可以采用箭头函数来改写。例如:var p1 = new Promise((resolve,reject)=>{})

  • 在函数体的内部, 一般会执行异步代码,然后根据情况来调用resolve()或者是reject() 。调用resolve或者是reject后会产生什么样的后果,在后面小节介绍。 当然了,再次强调一下resolve和reject只是形参名,可以改写成其它的。

2、三种状态和值

一个Promise对象的状态可能是如下三种之一:pending,resolved,rejected

(1)初始态pending

pending。它的意思是 "待定的,将发生的",相当于是一个初始状态。 创建Promise对象时,且没有调用resolve或者是reject方法,相当于是初始状态。这个初始状态会随着你调用resolve,或者是reject函数而切换到另一种状态。

var p = new Promise((ok,err)=>{ console.info('发呆.....' )})
console.dir(p)

这个初始状态就是你对别人说出承诺的那个瞬间,此时,大家都不知道这个承诺是否能兑现。

(2)成功态resolved/fulfilled

resolved。表示解决了,就是说这个承诺实现了。 要实现从pending到resolved的转变,需要在 创建Promise对象时,在函数体中调用了resolve方法(即第一个参数)。

var p = new Promise((resolve,reject)=>{ resolved();})
console.dir(p)

(3)失败态rejected

rejected。拒绝,失败。表示这个承诺没有做到,失败了。要实现从pending到rejected的转换,只需要在创建Promise对象时,调用reject函数。

var p = new Promise((resolve,reject)=>{ reject()} )
console.dir(p)

(4)三种状态小结

<1>状态是可转化的

最初创建promise对象时,默认状态是pending,如果在函数体内部调用了第一个参数对应的函数,则状态变成了resolved;如果调用了第二个参数对应的函数,则状态变成了rejected。

 pending -----  resolve() --> resolved;
 pending -----  reject()  --> rejected;

<2>状态转换是不可逆的

一旦从pending ---> resolved(或者是rejected),就不可能再回到pending,也不能由resolved变成rejected。

(5)promise的值

一个promise对象除了状态之外,还有promiseValue。在构造器中,这个值在调用resolve和reject方法时传入。

例如:

var p = new Promise( (resolve,reject) => { resolve(123); } );
//  此时,prommise对象p的状态是 resolved,值是123。
console.dir(p)

(6)promise的兑现

在构造器的函数体中,一旦状态发生了变化,就会进行then,或者是catch中去,同时把promiseValue传入对应的函数。

具体来说:

  • 状态从pending变成resolved,进入then中,调用函数,并传入此时的promiseValue(就是调用resolve时传入的实参)

  • 状态从pending变成rejected,进入catch中,调用函数,并传入此时的promiseValue(就是调用reject时传入的实参)

根据实际情况的需要,也可以不加上finally()。

二、使用promise改造回调函数

下面我们回到具体的问题:如何对现有的,通过回调函数来实现异步操作的代码进行改写。

第一步:建立模板。这里的模板是指固定的套路:写一个空函数,在函数体中的创建一个promise对象,并返回。

function fnName(){
    var p = new Promise((resolve,reject)=>{})
    return p;
}

第二步:把异步功能写入构造器中,根据实际来决定是否调用resolve,reject。

function fnName(){
    var p = new Promise((resolve,reject)=>{
        // 这里写具体的代码,并在某个恰当的时机去调用resolve和reject函数。
    })
    return p;
}

第三步:调用函数。

通过fnName().then().catch() 结构来调用这个函数。

示例: nodejs中的文件读取

原来的写法:

const fs = require('fs');

fs.readFile("1.txt", 'utf8', (err, data) => {
    if (err) {
    	reject(err);
    } else {
    	resolve(data);
    }
});

改写之后:

const fs = require('fs');
function readFile(filename) {
  return new Promise((resolve, reject) => {
    fs.readFile(filename, 'utf8', (err, data) => {
      if (err) {
        reject(err);
      } else {
        resolve(data);
      }
    });
  });
}

readFile('./server.js1')
  .then(rs => {
    console.log(rs);
  })
  .catch(err => {
    console.log('err', err);
  }); 

三、then的格式及执行逻辑

then方法的作用是为Promise对象添加状态改变时的回调函数。下面从其调用格式,执行逻辑及返回值三个方面来介绍

(1)then的格式

它可以写两个参数,如下:

// p 是一个promise对象
p.then(函数1[,函数2])

它的两个参数都是函数。

  • 第一个参数是resolved状态的回调函数。当p的状态从pending变成了resolved之后,函数1会执行。

  • 第二个参数是rejected状态的回调函数。 当p的状态从pending变成了rejected之后,函数2会执行。

  • 其中第二个参数是可选的,如果只写一个参数的话就是如下:

promise对象.then(函数1)

(2)执行逻辑

以如下代码为例:

var p = new Promise((resolve,reject)=>{
   resolve(val1);
    // or reject(val2)
})

p.then((okVal)=>{
    console.info("成功");
    console.log(okValue);
}, (errVal)=>{
    console.info("失败");
    console.log(errValue);
})

它的两个参数都是函数,其执行逻辑是:

  • 如果promise对象的状态是resolved,则then()会执行第一个函数,并传入当前的PromiseValue(即上面的val1);

  • 如果promise对象的状态是rejected,则then()会执行第二个函数,并传入当前的PromiseValue(即上面的val2);

  • 特别地,如果promise对象的状态是rejected,且此时then方法并没有设置第二个参数,就会向外抛出一个错误,错误的提示大约是Uncaught (in promise)

示例代码1:

var p = new Promise((resolve,reject)=>{
	resolve(1)//主动调用resolve,并传入
})
// 此时,P的状态是resolved,且值promiseValue 是1.
p.then((res)=>{
    // 因为p的状态是resolved,所以自动执行then的第一个参数,并且把promisevalue传进来。
	console.log("then,ok",res)
})

执行输出结果是:then,ok,1

示例代码2:

var p = new Promise((resolve,reject)=>{
	reject(2)//主动调用reject,并传入实参
})
// 此时,P的状态是rejected,且值promiseValue 是2.

p.then((res)=>{
    // 因为p的状态是resolved,所以这句代码不会执行。
	console.log("then,ok",res)
},(err)=>{
    // 因为p的状态是rejected,所以自动执行then的第二个参数,并且把promisevalue传进来。
	console.log("then,err",err)
})

 执行输出结果是:then,err,2

(3)then的返回值

then()方法的返回值也是一个promise对象,所以它支持链式写法。但是要注意的是它的返回值是一个新的promise对象,与调用then方法的并不是同一个对象。

看下如下代码:

var p1 = new Promise(()=>{});
var p2 = p1.then(function f_ok(){}, 
	function f_err(){}
); 
// p2也是一个promise对象。

console.log(p1 === p2); // false

如上代码可以说明p1.then()的结果是一个与p1不同的promise对象。换句话说,then()会封装一个全新的promise对象p2。那既然 p2也是一个promise对象,那么,p2的状态(promiseStatus)和值(promiseValue)分别是什么?

p2的状态及promiseValue按如下规则来确定

  • 如果p1的状态是pending,则p2的状态也是pending。

  • 如果p1的状态是resolved,then()会去执行f_ok,则p2的状态由f_ok的返回值决定。

    • 如果f_ok返回值不是promise对象,则p2的状态是resolved,且p2的promiseValue就是f_ok函数的return值。

    • 如果f_ok返回值是一个promise对象,则p2的状态及promiseValue以这个promise对象为准。

    • 如果f_ok这个函数内部发生了错误(或者是用户主动抛出错误),则p2的状态是rejected,且p2的promiseValue就是这个错误对象。

  • 如果p1的状态是rejected,then()会去执行f_err,则p2的状态由f_err的返回值决定。

    • 如果f_err返回值不是promise对象,则p2的状态是resolved,且p2的promiseValue就是f_err函数的return值。

    • 如果f_err返回值是一个promise对象,则p2的状态及promiseValue以这个promise对象为准。

    • 如果f_err这个函数内部发生了错误(或者是用户主动抛出错误),则p2的状态是rejected,且p2的promiseValue就是这个错误对象。

特殊地,如果f_ok()中并没有return语句,则相当于是 return undefined。

四、catch的格式及用法

catch()的格式及用法

Promise.prototype.catch 是 Promise.prototype.then(null, reject)的别名,用于指定当promise对象的状态从pending变成rejected的回调函数 。

var p1 = new Promise((resolve,reject)=>{
	reject('s')
});

p1.catch(function(err){
	console.log(err);
})

// 与下面的代码等价
p1.then(null, function(err){
	console.log(err);
})

单独使用catch没有什么意义,它一般与then一块使用。如下:

new Promise((resolve,reject)=>{

}).then(function(result){
	// 如果promise对象的状态是resolved的,就会到这里来,并且result会接收promiseValue的值
}).catch(function(err){
	// 如果promise对象的状态是rejected的,就会到这里来,并且err会接收promiseValue的值
})

// 上面的代码如何拆分来写的话,等价于:
var p1 = new Promise((resolve,reject){

});
var p2 = p1.then(function(result){

});
var p3 = p2.catch(function(err){

})

catch的返回值

catch的返回值仍是一个promise对象,确定它的值的方式与then(null,(errVal)=>{ })的方式一致。

五、promise的链式调用

示例1

function do1() {
    console.log("任务1");
}
function do2() {
    console.log("任务2");
}
function do3() {
     console.log("任务3");
}
function do4() {
    console.log("任务4");
}

var p = new Promise((resolve,reject)=>{ resolve()})
p.then(do1)
 .then(do2)
 .then(do3)
 .then(do4);

结果输出是:任务1,任务2,任务3,任务4

分析:

var p1 = p.then(do1);
var p2 = p1.then(do2)
var p3 = p2.then(do3)
var p4 = p3.then(do4)

第一步:由于p的状态是resolved,所以p.then(do1)中,do1函数会执行。输出任务1

第二步:确定p1的状态。按前面关于then的部分的介绍,p1的状态由do1()来决定。因为do1并没有明确指定返回值,则返回值就是undefined. p1的状态就是resolved。

第三步:由于p1的状态是resolved,所以p1.then(do2)会继续执行do2。输出任务2 ,且p2的状态由do2来决定。与第二步的分析相同,p2的状态仍是resolved。

第四步:接下来看p3。由于p2的状态是resolved,所以p2.then(do3)会继续执行do3。输出任务2 ,且p3的状态由do3来定,仍是resolved。

最后:p3.then(do4)。由于p3的状态是resolved,所以执行do4。输出任务4

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ES6Promise是一种用于处理异步操作的对象。Promise对象可以表示一个异步操作的最终完成(包括成功和失败)及结果值的表示。它可以解决传统回调函数的问题,使代码更具可读性和可维护性。Promise对象有三种状态:pending(进行)、fulfilled(已成功)和rejected(已失败)。通过使用Promise对象,我们可以更好地控制和处理异步操作的结果。 ES6Promise相对于传统的回调函数具有以下优点: 1. 更好的代码结构和可读性:使用Promise可以使代码结构更清晰,减少了回调函数的层层嵌套。 2. 更好的错误处理:Promise可以通过catch方法捕获错误,并进行统一的错误处理,使错误处理更加简洁和方便。 3. 更好的异步流程控制:Promise可以通过then方法链式调用,实现更复杂的异步流程控制,避免了回调地狱的问题。 ES6Promise API包括以下方法: 1. Promise.resolve(value):返回一个解析后带有给定值的Promise对象。 2. Promise.reject(reason):返回一个带有拒绝原因的Promise对象。 3. Promise.all(iterable):返回一个Promise对象,该对象在iterable参数的所有Promise对象都已解析或拒绝后解析。 4. Promise.race(iterable):返回一个Promise对象,该对象在iterable参数的任何一个Promise对象解析或拒绝后解析。 5. Promise.prototype.then(onFulfilled, onRejected):添加解析和拒绝回调函数到Promise。 6. Promise.prototype.catch(onRejected):添加一个拒绝回调函数到Promise

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值