异步编程之Promise

在Promise出现前,一般是用回调函数来进行异步编程,但是当我们回调的次数非常多时,回调地狱就产生了,同时执行顺序混乱,这会不利于我们阅读代码。

Pormise就是为了解决回调函数导致的回调地狱问题而出现的

首先,Promise是什么呢?

Promise 语法上来说是一个对象,里面保存着在未来发生的事情的结果(即一个异步操作的结果),通过Promise 可以获取到异步操作的消息。

Promise实例的三种状态

pending(进行中)

fulfilled (已成功)

rejected(已失败)

Promise特点

(1)对象的状态不受外界影响。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这状态。

(2)一旦状态改变,就不会再变。Promise 对象的状态改变,只有两种可能:从pending变为fulfilled ,从pending变为rejected。只有发生其中一种状态变化,状态就不会再变了;这时就被称为 resolved(已定型),如果状态已经发生改变了,这时再对Promise对象添加回调函数,也会立即得到这个结果。

Promise缺点

(1)无法取消 promise,一旦新建它就会执行,无法中途取消。

(2)如果不设置回调函数,promise内部抛出的错误,不会反应到外部。

(3)当处于pending状态时,无法得知目前进展到哪一阶段(刚刚开始还是即将完成)。

Promise优点(相较于回调函数)

(1)解决了回调地狱的问题,使代码变得扁平可读且有利于维护

(2)更好的进行错误处理,通过reject方法把promise状态设置为rejected ,这样就可以在then中捕获或者在catch中捕获,然后执行 ’ rejected ‘ 的回调函数。

创建Promise对象实例的方式

1. new Promise() 创建对象

const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
   	resolve(value);
  } else {
    reject(error);
  }
});

2. Promise.resolve() 创建对象,且状态为fulfilled

const value = 'success' ;
Promise.resolve(value).then(value => {
	console.log(value) ;//success
})

3. Promise.reject() 创建对象,且状态为rejected

const value = 'error'
//第一种方式
Promise.reject(value).then(undefined, function (value) {
    console.log(value) ;//error
});

//第二种方式
Promise.reject(value).then(null, function (value) {
    console.log(value) ;//error
});

//第三种方式(推荐)
Promise.reject(value).catch(e => {
    console.log(e) ;//error
})

//第四种方式
Promise.reject(new Error("error!!!"));
Promise常用的方法

(1)Promise.prototype.then( )

then方法是定义在原型对象Promise.prototype上的

作用:为Promise实例添加状态改变时的回调函数

参数:

参数一:resolved状态的回调函数(可选),状态变为resolved时调用

参数二:rejected状态的回调函数(可选),状态变为rejected时调用

返回新的Promise实例,因此可以采用链式写法,在then方法后面再调用另一个then方法。

let promise = new Promise((resolve , reject) => {
	resolve('success') ;
}).then(value => {
	console.log('1:' , value) ;
	return Promise.resolve('success2') ;
}).then(value => {
	console.log('2:' , value) ;
})

//1: success
//2: success2   

(2) Promise.prototype.catch( )

Promise.prototype.catch( ) .then(null , rejection) .then(undefined , rejection) 的别名(推荐:把错误写在catch里面,因为它可以捕获前面then方法中出现的错误~)

作用:用于指定发生错误时的回调函数

参数:错误原因

注意:如果Promise已经变成resolved,再抛出错误是无效的

//第一种
const promise = new Promise(function(resolve, reject) {
	throw new Error('err');
});
promise.catch(error => {
	console.log(error);
});

//第二种
const promise = new Promise(function(resolve, reject) {
    try {
        throw new Error('err');
    } catch(e) {
        reject(e);
    }
});
promise.catch(error =>{
    console.log(error);
});

//第三种
const promise = new Promise(function(resolve, reject) {
	reject(new Error('err'));
});
promise.catch(error => {
    console.log(error);
});

(3)Promise.prototype.finally( )

作用:不管promise对象最后的状态是什么,都会执行的操作

finally( )方法没有参数,这说明了finally( )方法与状态无关,不依赖于Promise的结果

主要运用:如果不使用finally,则同样的语句需要为成功和失败两种情况各写一次,如果使用了finally方法,则只需要写一次

const promise = new Promise(function(resolve , reject) {
	// resolve('success') ;
	reject('text') ;
});
promise.then((value) => {
	console.log('1:' , value) ;
}).catch((error) => {
	console.log('err:',error) ;
}).finally(() => {
	console.log('end') ;
})

//Promise状态为 fulfilled 时,
// 1:success
// end

//Promise状态为 reject 时,
// err:text
// end

(4)Promise.all()

作用:将多个Promise实例包装成一个新的Promise实例

参数:Promise实例数组(如果数组中的元素不是Promise实例,会先调用 Promise.resolve( ) 方法,将元素转换为Promise实例,再进行下一步操作)

另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。

const p = Promise.all([p1, p2, p3]);

Promise.all 方法返回的新Promise实例的状态取决于p1、p2、p3的状态:

1、当p1、p2、p3 的状态都是fulfilled,p的状态才会变成fulfilled,这时p1、p2、p3 的返回值组成一个数组,传递给p的回调函数

2、只要p1、p2、p3 其中一个实例的状态变成 rejected,p的状态就会变成rejected,这时第一个被reject的实例的返回值,会传递给p的回调函数

// 当p1、p2、p3 的状态都是 fulfilled 时
const p1 = new Promise(function(resolve , reject) {
	resolve('p1:success') ;
})

const p2 = new Promise(function(resolve , reject) {
	resolve('p2:success') ;
})

const p3 = new Promise(function(resolve , reject) {
	resolve('p3:success') ;
})

const p = Promise.all([p1,p2,p3])
.then(value => console.log(value))
.catch(err => console.log(err))

// [ 'p1:success', 'p2:success', 'p3:success' ]
// 当p2的状态是rejected时
const p1 = new Promise(function(resolve , reject) {
	resolve('p1:success') ;
})

const p2 = new Promise(function(resolve , reject) {
	reject('p2: text') ;
})

const p3 = new Promise(function(resolve , reject) {
	resolve('p3:success') ;
})

const p = Promise.all([p1,p2,p3])
.then(value => console.log(value) )
.catch(err => console.log(err))

//p2: text

当p2实例状态变成rejected,并且p2实例有自己的catch方法,这时p2实例会执行catch方法,catch方法会返回一个新的Promise实例,p2指向的是这个新Promise实例,这个实例执行完catch方法后,也会变成fulfilled状态,导致Promise.all方法中的所有实例状态都变成了fulfilled,因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数

const p1 = new Promise(function(resolve , reject) {
	resolve('p1:success') ;
})

const p2 = new Promise(function(resolve , reject) {
	reject('p2: text') ;
}).catch(err => err);

const p3 = new Promise(function(resolve , reject) {
	resolve('p3:success') ;
})

const p = Promise.all([p1,p2,p3])
.then(value => console.log(value) )
.catch(err => console.log(err))

// [ 'p1:success', 'p2: text', 'p3:success' ]

如果 p2 没有自己的 catch 方法,就会调用Promise.all( )的catch方法。

(5)Promise.race()

作用:将多个Promise实例包装成一个新的Promise实例(和Promise.all ( ) 一样~)

运用:用于在规定时间内,如果请求没有返回结果,则返回失败,否则返回成功。

参数:Promise实例数组(如果数组中的元素不是Promise实例,会先调用 Promise.resolve( ) 方法,将元素转换为Promise实例,再进行下一步操作)(和Promise.all ( ) 一样~)

const p = Promise.race([p1, p2, p3]);

Promise.race 方法返回的新Promise实例的状态取决于p1、p2、p3的状态:(与Promise.all( )的区别)

只要 p1, p2, p3 中有一个实例率先改变状态 , p 的状态就变成和那个率先改变的实例的状态一样 ;并把率先改变状态的实例的返回值传递给 p 的回调函数。

const p1 = new Promise(function(resolve , reject) {
	setTimeout(() => resolve('p1:success'),1000) ; //最先改变状态
})

const p2 = new Promise(function(resolve , reject) {
	setTimeout(() => reject('p2: text'),2000) ;
}).catch(err => err);

const p3 = new Promise(function(resolve , reject) {
	setTimeout(() => resolve('p3:success'),3000) ;
})

const p = Promise.race([p1,p2,p3])
.then(value => console.log(value) )
.catch(err => console.log(err))

//p1:success
//我试验了一下,当它们时间一样时,还是输出p1,因为在同步执行中,p1也是最先执行的

还有剩下的方法,大家可以去看看ECMAScript 6 入门

学了辣么多方法,下面来练练手叭(题目网上找滴,侵删

1、

var promise = new Promise(function(resolve, reject){
  setTimeout(function() {
    resolve(1) ;
  }, 3000)
})
//分析一下一下三种方式的不同?
// 1
promise.then(() => {
  return Promise.resolve(2) ;
}).then((n) => {
  console.log(n) ;
});

// 2
promise.then(() => {
  return 2 ;
}).then((n) => {
  console.log(n) ;
});

// 3
promise.then(2).then((n) => {
  console.log(n) ;
});

1、输出2,Promise.resolved返回了一个新的Promise对象,并在下一个事件循环中执行then

2、输出2,直接返回2,不用等到下一个时间循环才执行then,直接在这个循环中执行

3、输出1,因为then和catch方法都期望一个函数作为参数,如果遇到非函数的参数,它们会发生Promise穿透现象,打印上一个Promise的返回值

输出结果:

//2
//2
//1

2、

Promise.resolve().then(() => {
  console.log('promise1');
  const timer2 = setTimeout(() => {
    console.log('timer2')
  }, 0)
});
const timer1 = setTimeout(() => {
  console.log('timer1')
  Promise.resolve().then(() => {
    console.log('promise2')
  })
}, 0)
console.log('start');

1、首先 Promise.resove( ).then( )是一个微任务,把它加入到微任务队列

2、接下来执行timer1,它是一个宏任务,把它加入到宏任务队列

3、输出start

4、第一轮宏任务执行完成,开始查看微任务队列中是否存在微任务

5、输出promise1,timer2是宏任务,将它加入到宏任务队列中

6、微任务执行完成,开始查看宏任务队列中是否存在宏任务

7、执行timer1,输出timer1Promise.resove( ).then( )是一个微任务,把它加入到微任务队列

8、执行微任务,输出promise2

9、最后执行宏任务timer2,输出timer2

输出结果

//start
//promise1
//timer1
//promise2
//timer2

3、

const promise = new Promise((resolve, reject) => {
    resolve('success1');
    reject('error');
    resolve('success2');
});
promise.then((res) => {
    console.log('then:', res);
}).catch((err) => {
    console.log('catch:', err);
})

1、首先执行resolve('success1'),执行完毕,promise的状态变成了resolved

2、根据Promise的特点可以知道,Promise的状态一旦发生变化,就不会再变

3、之后执行微任务promise.then( ),输出then: success1

4、

Promise.reject('err!!!')
  .then((res) => {
    console.log('success', res)
  }, (err) => {
    console.log('error', err)
  }).catch(err => {
    console.log('catch', err)
  })

1、首先执行Promise.reject( )

2、因为.then函数中存在两个参数,其中第一个参数是处理Promise成功状态下的函数,第二个参数是处理Promise失败状态下的函数;所以错误会直接被.then函数中的第二个参数捕获,输出error err!!!,而不会被后面的catch捕获

输出结果

//error err!!!

参考文章~

Promise面试题汇总

Promise 面试题整理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值