《十四》ES6+ 中的 Promise

Promise (承诺)是 ES6 引入的异步操作的一种解决方案。Promise 是一个类,用于封装异步操作并可以获取其成功或失败的结果。用同步的方式去写异步代码,链式调用相比于层层嵌套来说代码变得扁平了,解决了之前回调地狱的问题,但是还是需要维护一条调用链。

Promise 解决的不是回调函数的问题,而是层层嵌套的回调函数形成的回调地狱的问题。

传统异步操作的解决方案:

传统异步操作的解决方案,大部分是用回调函数或事件驱动。

  1. 回调函数:一个函数被作为参数传递到另一个函数里,在那个函数执行完后再执行。
    function fn(callback) {
    	setTimeout(() => {
    		let total = 0
    		for(let i = 0; i <= 10; i++) {
    			total += i
    		}
    		// 2. 传入的回调函数在异步操作执行完之后调用
    		callback(total)
    	}, 2000)
    }
    
    // 1. 传入一个回调函数,
    fn((total) => {
    	console.log(total)
    })
    
    // 层层嵌套的回调函数,形成了回调地狱
    ajax(url,{ //获取token
    	ajax(url,{ //获取用户信息
    		ajax(url,{ //获取用户相关新闻
     		...
    		})
    	})
    })
    
  2. 事件驱动:任务的执行不取决于代码的顺序,而取决于某一个事件是否发生。

Promise 的优缺点:

  1. 优点:
    • 有了 Promise,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
    • Promise 提供统一的接口,使得控制异步操作更加容易。
  2. 缺点:
    • 无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
    • 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
    • 当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

创建 Promise 实例对象:

创建 Promise 实例对象需要通过调用 Promise 类。Promise 类接受一个回调函数作为参数,该函数的两个参数分别是 resolve 和 reject,它们也是两个函数。resolve 函数的作用是将 Promise 的状态从进行中变为已完成,在异步操作成功时调用,并可传递参数出去;reject 函数的作用是将 Promise 的状态从进行中变为已失败,在异步操作失败时调用,并可传递参数出去。

// 1. 通过 new Promise() 创建一个 Promise 实例对象
const p = new Promise((resolve, reject) => {
	// 2. 传入一个回调函数作为参数,在此回调函数中执行异步操作。异步操作执行成功,调用 resolve() 方法通知 Promise 实例对象,then 方法的回调函数会被立即执行;异步操作执行失败,调用 reject() 方法通知 Promise 实例对象,catch 方法的回调函数会被立即执行
	setTimeout(() => {
		resolve('成功')
	}, 2000)
})

// 3. 调用 Promise 实例对象的 then 方法并传入一个回调函数,传入的回调函数会在调用 resolve() 后立即执行;调用 Promise 实例对象的 catch 方法并传入一个回调函数,传入的回调函数会在调用 reject() 后立即执行
p.then(res => {
	console.log(res)
}).catch(err => { 
	console.log(err)
})

创建 Promise 实例对象时,传入的回调函数会被立即执行。

const p = new Promise((resolve, reject) => {
  console.log('1')
  resolve()
});

p.then(() => {
  console.log('2')
});

console.log('3')

// 1
// 3
// 2

resolve() 方法的参数:

  1. resolve() 没有参数时,then() 方法中接收到的参数就是 undefined。
    const p = new Promise((resolve, reject) => {
    	resolve()
    })
    p.then(res => {
    	console.log(res) // undefined
    })
    
  2. resolve() 的参数是一个普通值时(非 Promise 类型,且不是 thenable 对象),then() 方法中接收到的参数就是原封不动的普通值。
    const p = new Promise((resolve, reject) => {
    	resolve('成功')
    })
    p.then(res => {
    	console.log(res) // 成功
    })
    
  3. resolve() 的参数本身就是一个 Promise 对象时,当前 Promise 对象的状态会由传入的 Promise 对象的状态决定,then() 方法中接收到的参数也会由传入的 Promise 对象决定。
    const paramsP = new Promise((resolve, reject) => {
    	resolve('成功')
    })
    
    const p = new Promise((resolve, reject) => {
    	resolve(paramsP)
    })
    p.then(res => {
    	console.log(res) // 成功
    })
    
  4. resolve() 的参数是一个 thenable 对象时(带有 then 方法的对象),会立即执行该对象的 then 方法,并且根据 then 方法的结果来决定当前 Promise 对象的状态和参数。
    const thenable = {
    	then: () => {
    		// 会打印 thenable。但因为这个 then 方法中既没有调用 resolve(),也没有调用 reject(),因此 p.then() 和 p.catch() 中的回调函数都不会被执行
    		console.log('thenable') // thenable
    	}
    }
    
    const p = new Promise((resolve, reject) => {
    	resolve(thenable)
    })
    p.then(res => {
    	console.log(res)
    }).catch(err => {
    	console.log(err)
    })
    
    const thenable = {
    	then: (resolve, reject) => {
    		// 会打印 thenable。因为这个 then 方法中调用了 reject(),因此 p.catch() 中的回调函数会被执行
    		console.log('thenable') // thenable
    		reject('失败')
    	}
    }
    
    const p = new Promise((resolve, reject) => {
    	resolve(thenable)
    })
    p.then(res => {
    	console.log(res)
    }).catch(err => {
    	console.log(err) // 失败
    })
    

reject() 方法的参数:

传入的参数无论是什么形态,都会直接作为已失败状态的参数传递到 catch() 方法的回调函数中。

Promise 的状态:

Promise 有三种状态:pending 进行中、fulfilled 已完成、rejected 已失败。Promise 状态的改变只有两种:调用 resolve() 后,由 pending 变为 fulfilled;调用 reject() 后,由 pending 变为 rejected。

const p = new Promise((resolve, reject) => {
	//1. 此时,p 的状态是 pending

	// 2. 如果调用 resolve(),p 的状态变为 fulfilled
	resolve()

	// 3. 如果调用 reject(),p 的状态变为 rejected
	reject()
})

Promise 的状态不受外界的影响,只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。并且 Promise 的状态一旦改变,就不会再变。

const p = new Promise((resolve, reject) => {
	// 调用了两次 resolve(),但是 then() 方法中的回调函数还是只会执行一次。因为第一次调用 resolve(),p 的状态就已经改变了,不会再变,也就不会再触发 then 方法中回调函数的执行
	resolve()
	resolve()
})
p.then(res => {
	console.log('成功')
}).catch(err => {
	console.log('失败')
})
const p = new Promise((resolve, reject) => {
	// 调用了一次 resolve(),一次 reject(),但是只有 then() 方法中的回调函数会被执行一次。因为第一次调用 resolve(),p 的状态就已经改变了,不会再变,也就不会再触发 catch 方法中回调函数的执行
	resolve()
	reject()
})
p.then(res => {
	console.log('成功')
}).catch(err => {
	console.log('失败')
})

Promise 的实例方法:

then() 方法:

then() 方法接收两个回调函数作为参数,这两个函数都接受 Promise 传出的值作为参数。第一个回调函数会在调用 resolve() 方法,Promie 对象的状态变为已完成后立即执行,第二个回调函数会在调用 reject() 方法,Promie 对象的状态变为已失败后立即执行,其中,第二个函数是可选的。

// 创建一个 Promise 实例对象
const p = new Promise((resolve, reject) => {
	// 执行异步操作,成功调用 resolve(),失败调用 reject()
	setTimeout(() => {
		resolve('成功')
	}, 2000)
})

p.then(res=>{
	console.log(res) // 成功
}, err=>{
	console.log(err)
});

可以多次通过 Promise 实例对象调用 then 方法来多次捕获 Promise 状态的改变。

const p = new Promise((resolve, reject) => {
	resolve('成功')
})
// 两个 then 方法中的回调函数都会被执行
p.then(res => {
	console.log(res) // 成功
})
p.then(res => {
	console.log(res) // 成功
})
then() 方法的返回值:

then() 方法本身是有返回值的,执行后会返回一个新的 Promise 对象,所以可以链式调用。

p.then(res=>{
	console.log(res)
}).then(res=>{
	console.log(res)
})

then() 方法执行后返回的 Promise 对象的状态,由 then 方法中的回调函数的返回值决定,return 后面的值默认会用 Promise 类包装一下。

  1. 如果回调函数中没有返回值,那么 then() 返回的 Promise 对象的状态为已成功,resolve 的值是 undefined。
    const p1 = new Promise((resolve, reject) => {
      resolve('p1')
    });
    
    p1.then(res => {
      console.log(res) // p1
       // 其实相当于 return new Promise(resolve => resolve(undefined)}
    }).then(res => {
       console.log(res) // undefined
    })
    
  2. 如果回调函数中返回一个普通值((非 Promise 类型,且不是 thenable 对象)),那么 then() 返回的 Promise 对象的状态为已完成,resolve 的值即为返回的值。
    const p1 = new Promise((resolve, reject) => {
      resolve('p1')
    });
    
    p1.then(res => {
      console.log(res) // p1
      // 其实相当于 return new Promise(resolve => resolve('p2')}
      return 'p2'
    }).then(res => {
      console.log(res) // p2
    })
    
  3. 如果回调函数中返回一个 Promise 类型的值,那么 then() 返回的 Promise 对象的状态即为内部 Promise 返回的状态,resolve/reject 的值即为内部 Promise 对象 resolve/reject 的值。
    const p1 = new Promise((resolve, reject) => {
      resolve('p1')
    })
    const p2 = new Promise((resolve, reject) => {
      resolve('p2')
    });
    
    p1.then(res => {
      console.log(res) // p1
      return p2
    }).then(res => {
      console.log(res) // p2
    })
    
  4. 如果回调函数返回一个 thenable 对象时(带有 then 方法的对象),会立即执行该对象的 then 方法,并且根据 then 方法的结果来决定返回的 Promise 对象的状态和参数。
    const p1 = new Promise((resolve, reject) => {
    	resolve('p1')
    })
    p1.then(res => {
    	console.log(res) // p1
    	// 返回的 thenable 对象的 then 方法中既没有调用 resolve(),也没有调用 reject(),因此返回的 Promise 对象的状态仍然是进行中
    	return {
    		then: () => {
    
    		}
    	}
    }).then(res => {
    	// 不会被执行
    	console.log(res)
    })
    
    const p1 = new Promise((resolve, reject) => {
    	resolve('p1')
    })
    p1.then(res => {
    	console.log(res) // p1
    	return {
    		then: (resolve) => {
    			resolve('p2')
    		}
    	}
    }).then(res => {
    	console.log(res) // p2
    })
    
  5. 如果回调函数中抛出错误,那么 then() 返回的 Promise 对象的状态为已失败,reject 的值即为抛出的错误。
    let p1 = new Promise((resolve, reject) => {
      resolve('p1')
    });
    
    p1.then(res => {
      console.log(res) // p1
      throw 'p2'
    }).then(res => {
    	console.log(res)
    }, err => {
    	console.log(err) // p2
    })
    

catch() 方法:

catch() 方法接收一个回调函数作为参数,该回调函数会在调用 reject() 方法,Promie 对象的状态变为已失败后立即执行,接收 reject() 传出的值作为参数。

const p = new Promise((resolve, reject) => {
	reject('失败')
})

p.catch(err=>{
	console.log(err) // 失败
})

Promise 的 rejected 状态只要没被 catch() 方法或者 then() 方法的第二个参数捕获过,就会一直向下传递,直到被捕获。

这一点比较特殊,虽然每次调用 then/catch 都会返回一个新的 Promie 对象,但只要 rejected 状态没被捕获过,就会一直向下传递。

const p = new Promise((resolve, reject) => {
	reject('失败')
})
p.then().then().then().catch(err => {
	console.log(err) // 失败
})

可以多次通过 Promise 实例对象调用 catch() 方法来多次捕获 Promise 状态变为已失败。

const p = new Promise((resolve, reject) => {
	reject('失败')
})
// 两个 catch 方法中的回调函数都会被执行
p.catch(err => {
	console.log(err) // 失败
})
p.catch(err => {
	console.log(err) // 失败
})
catch() 方法的返回值:

catch() 方法也是会返回一个新的 Promise 对象,所以在 catch() 方法后面也可以链式调用。

p.catch(err =>{
	console.log(err)
}).then(res =>{
	console.log(res)
})

catch() 方法执行后返回的 Promise 对象的状态,由 catch 方法中的回调函数的返回值决定,return 后面的值默认会用 Promise 类包装一下。

then() 方法的返回值完全一样。

finally() 方法:

finally() 方法是在 ES9 中新增的特性。当 Promise 状态发生变化时,不论如何变化都会执行。finally() 方法接收不到任何参数。

Promise 的静态方法:

Promise.resolve()

Promise.resolve():相当于 new Promise(),并执行 resolve 操作。可传入的参数和 new Promise() 中 resolve 操作的一样。返回一个已完成状态的 Promise 对象。

Promise.resolve(‘成功’)

// 相当于:
new Promise(resolve => resolve('成功'))

Promise.reject()

Promise.reject():相当于 new Promise(),并执行 reject 操作。可传入的参数和 new Promise() 中 reject 操作的一样。返回一个已失败状态的 Promise 对象。

Promise.reject('失败')

// 等价于:
new Promise(reject => reject('失败'))

Promise.all()

Promise.all():用于将多个 Promise 实例,包装成一个新的 Promise 实例。接收一个 Promise 的数组作为参数,返回一个新的 Promise 对象。

Promise.all()Promise.race()Promise.allSettled() 的参数其实不只是数组,任何可迭代的结构都可作为参数。
参数中若有成员不是 Promise 对象,内部会将其转变成 Promise 对象。

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

只有 p1、p2、p3 的状态都变为已完成,p 的状态才会变为已完成,此时 p1、p2、p3 的 resolve 的值会被组成一个数组,作为参数传递给 p 的 then() 方法。

只要 p1、p2、p3 中有一个的状态是已失败,p 的状态就会变为已失败,此时,第一个已失败的实例的 reject 的值,会被作为参数传递给 p 的 catch() 方法。

const p1= Promise.resolve('p1')
const p2= Promise.resolve('p2')
const p3= Promise.resolve('p3')

Promise.all([p1, p2, p3]).then(res=>{
    console.log(res) // ["p1", "p2", "p3"]
})
const p1= Promise.resolve('p1')
const p2= Promise.reject('p2')
const p3= Promise.reject('p3')

Promise.all([p1,p2,p3]).then(res=>{
    console.log(res)
}).catch(err => {
    console.log(err) // p2
})

Promise.any()

Promise.any():ES12 中新增了 Promise.any() 方法。用于将多个 Promise 实例,包装成一个新的 Promise 实例。接收一个 Promise 的数组作为参数,返回一个新的并且永远是 resolved 状态的 Promise 对象。

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

只要 p1、p2、p3 中有一个的状态是已完成,p 的状态就会变为已完成,此时,第一个已完成的实例的 resolve 的值,会被作为参数传递给 p 的 then() 方法。

只有 p1、p2、p3 的状态都变为已失败,p 的状态才会变为已失败,p 的 catch() 方法会接收到一个默认的参数 AggregateError: All promises were rejected

const p1= Promise.reject('p1')
const p2= Promise.resolve('p2')
const p3= Promise.resolve('p3')
Promise.any([p1,p2,p3]).then(res=>{
  console.log(res) //  p2
}).catch(err => {
	console.log(err)
})
const p1= Promise.reject('p1')
const p2= Promise.reject('p2')
const p3= Promise.reject('p3')
Promise.any([p1,p2,p3]).then(res=>{
  console.log(res)
}).catch(err => {
	console.log(err) // AggregateError: All promises were rejected
})

Promise.race()

Promise.race():用于将多个 Promise 实例,包装成一个新的 Promise 实例。接收一个 Promise 的数组作为参数,返回一个新的 Promise 对象。

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

只要 p1、p2、p3 中有一个实例率先改变状态,p 的状态就会跟着改变,那个率先改变的 Promise 实例的返回值,会作为参数传递给 p 的后续方法。

const p1= Promise.resolve('p1')
const p2= Promise.resolve('p2')
const p3= Promise.resolve('p3')
Promise.race([p1,p2,p3]).then(res=>{
     console.log(res) //  p1
 })

const p1= Promise.reject('p1')
const p2= Promise.resolve('p2')
const p3= Promise.resolve('p3')
Promise.race([p1,p2,p3]).then(res=>{
    console.log(res);
 }).catch(err=>{
 	console.log(err) // p1
 })

Promise.allSettled()

Promise.allSettled():ES11 中新增了 Promise.allSettled() 方法。用于将多个 Promise 实例,包装成一个新的 Promise 实例。接收一个 Promise 的数组作为参数,返回一个新的并且永远是 resolved 状态的 Promise 对象。

Promise.allSettled() 的状态永远都是已完成,与传入的 Promise 对象参数的状态无关。只会记录下每个 Promise 的状态和结果。

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

当 p1、p2、p3 全部处理完成后,可以拿到每个 Promise 的状态和结果。

const p1= Promise.resolve('p1')
const p2= Promise.reject('p2')
const p3= Promise.resolve('p3')

Promise.allSettled([p1,p2,p3]).then(res=>{
    console.log(res)
})

请添加图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值