Promise
作用: 多个有依赖关系的异步任务会形成回调地狱,Promise用于解决回调地狱的问题
- Promise的状态明确之后无法更改
- then方法中指定的回调函数会进入到回调队列中排队。
- Promise对象的then方法会返回全新Promise对象从而实现链式调用,后面的then方法在为上一个then返回的Promise注册回调
- 前面then方法中回调函数的返回值会作为后面then方法回调的参数
- 如果回调中返回的promise,后面then方法的回调会等待他的结束。
- 在Promise的执行过程中遇到主动抛出的异常或者遇到错误就会执行失败时候的回调
<script>
const p = new Promise(function(resolve,reject){
resolve('成功'); //同时书写成功和失败时执行第一个
//reject(new Error('promise rejected'));
})
console.log(p) /* Promise {<fulfilled>: "成功"}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "成功" */
//方式1.通过then方法指定成功和失败后的回调
p.then(function(value){
console.log('resolved',value)
},function(error){
console.log('rejected',error )
})
//方式2.在then方法之后使用catch设置失败回调
p.then(function(value){ //可以接受resolve函数传来的数据
console.log('成功了,哈哈',value);//成功了,哈哈 成功
}).catch(function(){//可设置value参数
console.log('失败');
})
</script>
then方法返回的是全新的promise对象,方式1是给p指定失败的回调,方式2是给then方法返回的promise指定回调。由于是同一个promise链条,异常会传递到下来。
ajax(successUrl)
.then(function(res){
console.log(res)
return ajax(failUrl)
},function(err){ //只能接收第一个ajax的回调而无法接受到返回的ajax失败的回调
console.log(err)
})
ajax(successUrl)
.then(function(res){
console.log(res)
return ajax(failUrl)
})
.catch(function(err){ //第二个ajax返回的异常会向后传递,直至被捕获,所以可以接收到失败的回调
console.log(err);
})
Promise应用
常用于封装ajax,Node文件读写
封装ajax函数
const successUrl=''
const failUrl=''
//定义发送ajax请求函数
function ajax(url){
return new Promise(function(resolve,reject){
const xhr =new XMLHttpRequest();
xhr.open('GET',url)
xhr.responseType = 'json'
xhr.onreadystatenchange = function()
xhr.onlad = function(){
if(this.status === 200){
//当请求正常结束,并响应接受完毕,调用resolve并传入数据
resolve(this.resonse)
}else{
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
//调用ajax请求失败
ajax(failUrl)
.then(function(res){
console.log(res)
})
.catch(function(err){ //由于能直接打点调用catch方法所以.then的返回值为Promise对象
console.log(err);
})
//调用ajax请求成功以后再发请求。
//ajax().then(function(){请求成功代码return ajax()发送下次请求}, function(){失败后执行}).then(function(){第二次请求成功执行代码}, function)
ajax(successUrl)
.then(function(res){
console.log(res,1)
return ajax(successUrl) //设置回调函数再次发送请求
},function(err){ //then的第二个参数可以写失败的函数
console.log(err)
})
.then(function(res){ //这个then处理的是第二次请求的结果
console.log(res,2)
return ajax(successUrl)
})
.then(function(res){ //这个then处理的是第三次请求的结果
console.log(res,3)
})
Promise静态方法
只能通过类本身调用,而不能在类的实例上调用
Promise.resolve() 把一个值转化为一个promise对象
Promise.resolve('foo')
.then(function(value){
console.log(value) // fooo
})
//等价于
new Promise(function(resolve, reject){
resolve('foo')
})
Promise.reject()快速创建一定是失败的promise对象
Promise并行执行
Promise.all()可以将内部的Promise全部执行完毕以后返回新的promise对象
var promise = Promise.all([
ajax(url1),
ajax(url2),
'a',
...
])
promise.then(function (values){
console.log(valuse) //得到请求返回的数据组成的数组
}).catch(function(error){
console.log(error) //只要有一个promise失败就会返回失败的回调
})
Promise.all()方法输出的执行顺序是和调用顺序相同,即使内部同时存在同步和异步调用。内部调用的方法全部成功才会成功
Promise.race()方法
第一个任务结束时和就执行
const request = ajax(url)
const timeout = new Promise((resolve, reject) =>{
setTimeout(() => reject(new Error('timeout')),500)
})
Promise.race([request, timeout])
.then(value =>{console.log(value)}
.catch(error =>{console.log(error)})
Promise 执行时序
宏任务&微任务
回调队列中的任务称为宏任务,宏任务执行过程中可以加上一些额外需求(微任务),Promise的回调作为微任务执行 。process.nextTick、MutationObserve和promise为微任务,setTimeout(),setInterval(),setImmediate(),和注册事件为宏任务
console.log('global start')
setTimeout(()=>{console.log('settimeout')},0)
Promise.resolve()
.then(()=>{console.log('promise')})
.then(()=>{console.log('promise 2')})
console.log('global end')
// global start global end promise promise 2 settimeout
微任务使用情景
微任务在本轮事件循环的调用栈之后执行,早于时间处理函数和定时器的回调。可以减少操作中用户的感知时间;确保任务顺序的一致性;批量操作优化
Generator异步方案
Generator使用语法
定义时在函数名之前加* ,调用时不会立即执行生成器,而是得到生成器对象,使用next方法开始执行。
function * foo(){
console.log('start')
yield 'foo' // yield 不会像return一样中断退出执行,下一调用generator.next()方法时会从yield开始执行
// const res = yield 'aaa'
// console.log(res)
}
const generator = foo()
const result = generator.next()
console.log(result) //通过value将yield返回的值返回,并且返回的值中包含done属性,记录生成器是否执行完毕
//generator.next('bar') //传入的参数作为yield的返回值
generator.throw()方法可以在生成器内部抛出一个异常。
function * foo1(){
try {
const res = yield 'foo'
} catch(e){
console.log(e)
}
}
const generator = foo1()
const result = generator.next()
generator.throw(new Error('Generator error'))
Generator实现异步调用
function ajax(url){....}
function * main(){
try{
const users = yield ajax(successUrl1)
console.log(users)
const posts = yield ajax(successUrl2)
console.log(posts)
}catch(e){
console.log(e)
}
}
const g = main()
function handleResult(result){
if(result.done) return //如果执行完毕就返回
result.value.then(data =>{ //成功响应的回调
handleResult(g.next(data))
},error => { //请求失败的回调
g.throw(error)
})
}
handleResult(g.next())
Async异步函数
为避免多次链式调用导致代码结构复杂,使更接近与同步执行。使用时,在函数之前书写async 即可变为异步函数。所有的函数都可变为async函数async返回promise对象。需要搭配await使用。await将yeild替换。await之后如果不是promise 直接返回对应的值。await之后的代码在微任务队列中执行
const sucUrl=''
const faiUrl=''
function ajax(url){
return new Promise(function(resolve,reject){
const xhr =new XMLHttpRequest();
xhr.open('GET',url)
xhr.responseType = 'json'
xhr.onreadystatenchange = function()
xhr.onlad = function(){
if(this.status === 200){
//当请求正常结束,并响应接受完毕,调用resolve并传入数据
resolve(this.resonse)
}else{
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
async function fn(){
const data1 = await ajax(successUrl)
console.log(data1,1) //object 1 await之后的代码在微任务队列上执行
const data2 = await ajax(successUrl)
console.log(data2,2) //object 2 await之后的代码在微任务队列上执行
const data3 = await ajax(failUrl) //失败时无法处理并且中断执行第四个也无法输出
console.log(data3,3)
const data4 = await ajax(failUrl)
console.log(data4,4)
}
fn();
TryCatch
在使用awit和 async的过程中会需要对失败情况做异常捕获的处理。
try{
const data1 = await ajax(successUrl)
console.log(data1,1)
} catch{
console.log('报错信息',err1)
}
try{
const data2 = await ajax(failUlr)
console.log(data2,2)
} catch{
console.log('报错信息',err2)
}
Promise源码分析
Promise类核心逻辑实现
- Promise是一个类,执行这个类时需要传递一个执行器(executor)进去,执行器立即执行
- Promise有三种状态,等待(pending)、成功(fulfilled)和失败(rejected),状态确定无法更改
- resolve和reject函数更改函数状态
- then 方法内部判断状态,状态成功调用成功的回调函数。then方法是被定义在原型对象中的
- then成功回调后有一个参数,标识成功之后的值,失败回调有一个参数标识失败后的原因
const PENDING = 'pending' //等待状态
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise{
constructor (executor){
try{
exectuor(this.resolve, this.reject)
}catch(e){
this.reject(e)
}
}
//promise状态
status = PENDING;
//成功之后的值
value = undefined;
//失败之后的原因
reason = undefined;
//成功时的回调
successCallback = [];
//失败时的回调
failCallback = [];
resolve = value => {
//如果状态不是等待,阻止状态向下执行
if(this.status !== PENDING) return;
//更改状态为成功
this.status = FULFILLED;
//保存成功之后的值
this.value = value;
//异步执行resolve方法
while(this.successCallback.length) this.successCallback.shift()()
}
reject = reason => {
if(this.status !== PENDING) return;
this.status = REJECTED;
this.reason = reason;
while(this.failCallback.length) this.failCallback.shift()();
}
then (successCallback,failCallback) {
successCallback = successCallback ? successCallback : value => value
failCallback = failCallback ? failCallback : reason => {throw reason}
let promise2 = new Promise((resolve,reject)=>{
//判断状态
if(this.status === FULFILLED) {
setTimeout(()=>{ //由于promise2是在new Promise执行完成才存在,所以需要让其异步执行
try{
let x = successCallback(this.value)
//需要判断x是值还是promise对象,如果是promise对象,查看promise对象返回的结果决定调用resolve还是reject
resolvePromise(promise2,x,reslove,reject)
}catch(e){
reject(e)
}
},0)
}else if (this.status === REJECTED){
setTimeout(()=>{ //由于promise2是在new Promise执行完成才存在,所以需要让其异步执行
try{
let x = failCallback(this.reason)
//需要判断x是值还是promise对象,如果是promise对象,查看promise对象返回的结果决定调用resolve还是reject
resolvePromise(promise2,x,reslove,reject)
}catch(e){
reject(e)
}
},0)
}else {
//异步执行时未指明成功或者失败,将状态暂时存储
this.successCallback.push(()=>{
setTimeout(()=>{ //由于promise2是在new Promise执行完成才存在,所以需要让其异步执行
try{
let x = successCallback(this.value)
resolvePromise(promise2,x,reslove,reject)
}catch(e){
reject(e)
}
},0)
})
this.failCallback.push(() => {
setTimeout(()=>{ //由于promise2是在new Promise执行完成才存在,所以需要让其异步执行
try{
let x = failCallback(this.value)
resolvePromise(promise2,x,reslove,reject)
}catch(e){
reject(e)
}
},0)
})
}
})
return promise2
}
catch(failCallback){
return this.then(undefined,failCallback)
}
finally(callback){
return this.then(value=>{
return MyPromise.resolve(callback()).then(()=>value)
},reason=>{
return MyPromise.resolve(callback()).then(()=>{throw reason})
})
}
//定义promise的all方法,all方法传入存放函数或者普通值的数组,输出与传入函数相同顺序的执行结果
static all(array){
let result = [];
let index = 0;
return new Mypromise((resolve,reject) => {
//处理all中函数的方法
function addData(key,value){
result[key] = value
index++;
if(index == array.length) resolve(result)
}
for(let i=0; i<array.length; i++){
let current = array[i]
//判断all中是普通值还是函数
if(current instanceof Mypromise){
current.then(value => addData(i,value),reason => reject(reason))
}else {
//普通值放到结果数组中
addData(i,current)
}
}
})
}
// 定义resolve方法用于将传入的内容转为promise对象
static resolve(value){
if(value instanceof Mypromise) return value
return new MyPromise(resolve => resolve(value))
}
}
function resolvePromise(promise2,x,resolve,reject){
//如果自己返回自己形成循环则会报错
if(promise2 === x){
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if(x instanceof MyPromise){
//是一个promise对象
x.then(resolve,reject) //同x.then(value=>resolve(value),reason=>reject(reason))
}else{
//是一个值
resolve(x)
}
}