第一部分、什么是Promise ?
Promise是ES6中提供的一个异步编程的解决方案,Promise本身是一个构造函数
typeof Promise // function
一般情况下 我们在开发中会使用 new Promise() 调用构造函数,创建一个新的Promise对象, Promise对象有两个特点
1、对象的状态不受外界影响。Promise
对象是一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。
只有异步操作的结果,可以决定Promise是哪一种状态,任何其他操作都无法改变这个状态
2、一旦Promise状态改变,就不会再有变化,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled 或者 从pending变为rejected。只要这两种情况发生,状态就不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的
使用Promise的好处,就是在处理异步程序时,将异步操作队列化,按照期望的顺序执行,返回符合预期的结果,这样即使是多重异步操作,也可以方便的使用Promise进行链式调用
3、Promise
也有一些缺点。
首先,无法取消Promise
,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise
内部抛出的错误,不会反应到外部。第三,当处于pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
第二部分、循序渐进的介绍Promise的用法
1、最简单的用法
const p = new Promise((resolve, reject) => { // 在这里执行一个异步操作吧 setTimeout(function(){ console.log(1234) resolve(1234) }, 3000) }) // 创建这个Promise之后, 3秒后 会控制台输出1234,并且3秒后得到了一个结果,异步程序返回了1234这个结果,那么 p.then((res) => { console.log(res) }) // 控制台会输出1234 ,promise的then方法 会再次返回一个promise,不过值默认undefined p.then((res) => { console.log(res) }).then((res)=>{ console.log(res) }) // 1234, undefined
2、reject的用法
const p2 = new Promise((resolve, reject)=>{ // 执行一个异步操作 setTimeout(function(){ console.log(1024) reject(1024) }, 3000) }) p2.then(res=>{ console.log(res) }).catch(res=>{ console.error(res) }) // 3秒之后 输出错误 1024
3、all的用法, 多个异步操作进行数组形式的返回值处理,当所有的异步都resolve时,可以执行then操作,当其中一个或多个reject时,所有的异步都会停止调用,直接返回最早发生错误的那个结果
const pa = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pa') resolve('pa') },3000) }) const pb = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pb') resolve('pb') },4000) }) const pc = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pc') resolve('pc') },5000) }) const pd = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pd') reject('pd') },5000) }) Promise.all([pa, pb, pc]).then((res)=>{ console.log('成功' + res) }) // 3秒后 pa 4秒后pb 5秒后 pc pd 然后输出成功pa,pb,pc Promise.all([pa, pb, pd]).then((res)=>{ console.log('成功' + res) }).catch(err=>{ console.log('失败' + err) }) // 输出失败pd
4、race的用法, 语法和all()一样,但是返回值有所不同,race根据传入的多个Promise实例,只要有一个实例resolve或者reject,就只返回该结果,其他实例不再执行,也就是说多个异步程序,只返回响应最快的那个异步程序,不论成功或者失败,就把最快响应的返回值返回,后面的异步程序不再执行
const pa = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pa') resolve('pa') },3000) }) const pb = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pb') resolve('pb') },4000) }) const pc = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pc') resolve('pc') },5000) }) const pd = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pd') reject('pd') },5000) }) const pe = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pe') reject('pe') },2000) }) const pf = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pf') resolve('pf') },2000) }) Promise.race([pa, pe, pf]).then(res=>{ console.log('成功' + res) }).catch(res=>{ console.log('失败' + res) }) Promise.race([pd, pe]).then().catch(res=>{ console.log('失败' + res) }) // 2秒后 输出 失败 + pe // 2秒后 输出 失败 + pe
5、resolve方法,
Promise的resolve方法,用于将非Promise类型的对象,转为Promise对象,这样就可以调用Promise原型对象上的方法了
Promise.resolve(x) 上面的写法等价于 new Promise(resolve => { resolve(x)})
resolve的参数分为几种不同的情况:
(1)没有参数,如果没有参数,这直接返回一个resolved状态的Promise对象
const p = Promise.resolve() 相当于 const p = new Promise(resolve => { resolve undefined }) p.then(res=>{ console.log(res) }) // 输出 undefined
(2) 参数是一个不具有then方法的对象,或者是一个基数数据类型的值,则Promise.resolve
方法返回一个新的 Promise 对象,状态为resolved,值为指定值
const p = Promise.resolve('pro') p.then((x) =>{ console.log(x) }) // 输出 'pro'
(3) 参数是一个具有then方法的对象,Promise.resolve
方法会将这个对象转为 Promise 对象,然后就立即执行thenable
对象的then
方法。
const obj = { then: function(resolve, reject) { resolve(100) } } const p = Promise.resolve(obj) p.then((res) => { console.log(res) }) // 输出 100
(4)参数是一个Promise对象,那么将原封不动的返回这个Promise对象
6、reject方法
reject与resolve方法基本类似,但是要注意一种情况,就是当参数是一个thenable对象时
const thenable = { then(resolve, reject) { reject('出错了'); } }; Promise.reject(thenable) .catch(e => { console.log(e === thenable) }) // true
7、finally方法,finally
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
finally
方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled
还是rejected
。这表明,finally
方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。finally
本质上是then
方法的特例。
const p = new Promise((resolve, reject)=>{ const a = Math.ceil( Math.random() * 10 ) setTimeout(function(){ if(a > 5) { resolve(a) } else { reject(a) } },3000) }) p.then(res=>{ console.log(res) }).catch(err=>{ console.error(err) }).finally(()=>{ console.log('结束') }) // 无论是resolve 还是 reject都会 执行 finally // finally的 polyfill也很简单 Promise.prototype.finally = function (callback) { let P = this.constructor; return this.then( value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => { throw reason }) ); };