目的
首先,Promise是ES6新增的内置类(new Promise), 并且Promise 是一个"承诺"设计模式,主要目的是用来解决JS异步编程中的“回调地狱”(有效的管控异步编程).
Promise 三种角色
- 作为
普通函数
Promise() // Promise constructor cannot be invoked without 'new'
new Promise() // Uncaught TypeError: Promise resolver undefined is not a function 不能为空
new Promise([executor]) // executor函数 是一个可执行函数
2. 作为构造函数
-
实例: 作为实例的话有两个特殊私有属性的值
- [[PromiseState]] promise状态: pending、fulfilled/resolved、 rejected
- [[PromiseResult]] promise的值
-
Promise.prototype 原型
- then
- catch
- finally
3. 作为
普通对象
+ reject
+ resolve
+ all
+ race
为什么说Promise是异步呢?
-
Promise 是用来管控异步编程的,new Promise本身不是异步的,执行它的时候会立即把executor函数执行(只不过我们经常在executor中管控一个异步操作)
-
resolve/reject :传递给executor函数的参数(参数值都是函数)
-
promise初始状态是pending 初始值是undefined
-
resolve([value]) 修改promise状态为fulfilled / resolved 成功状态,并且改变其值为[value]
-
reject([reason]) 修改promise状态为rejected 失败状态,并且改变其值为[reason]
-
一旦状态发生改变,都不能改变为其他状态了
-
基于then存放两个回调函数,状态为成功后调用第一个回调函数执行,形参的值就是[[PromiseResult]],状态为失败则调用第二个回调函数执行。。。
-
Promise中的异步指的是resolve/reject执行
- 执行两个方法的时候不仅仅是修改状态和值,还要通知then存储的两个回调函数中的一个执行
- 执行两个方法的时候,需要先等待promise已经基于then把方法存储完毕,有方法后才会去执行
9.如果executor函数执行报错,状态也会变为失败态,并且改变其值为失败的原因
let p = new Promise((resolve,reject)=> {
// resolve('ok')
console.log(1)
})
console.log(2) // 这个的输出顺序依然是 1 2
p.then(value => {
console.log(‘成功执行’,value);
}, reason => {
console.log(‘失败执行’,reason);
})
then 链
-
基于.then 注册成功或者失败执行的回调函数,它的返回结果是一个新的promise实例
-
new Promise([executor]) 返回的实例的状态和value是根据resolve/reject执行。再或者 [executor]函数执行是否报错来决定的
-
.then(...) 返回的实例的状态和value是根据 .then注册的两个方法,不论哪一个方法执行,执行的返回结果和是否报错来决定状态和value的。=>不论哪一个方法执行,只要不报错,状态就是成功,报错状态则为失败,方法的返回值(或者报错原因)就是新的实例的value值;
-
特殊:如果返回的是一个新的promise实例(Promise.reject([reason])),则当前实例的状态和value决定了P2的状态和value
let p = new Promise((resolve, reject) => {
// resolve('ok')
reject('NO')
})
let p2 = p.then(value => {
console.log('成功执行p', value);
console.log(a);
return '@'
}, reason => {
console.log('失败执行', reason);
return '@@'
})
let p3 = p2.then(value => {
console.log('成功执行p2', value);
// console.log(a); 这个就是报错
// 如果有报错,按报错的,
// 若执行没有报错,而且返回一个新的promise实例,则当前实例的成功和失败,直接影响p3的结果
return Promise.reject('11')
}, reason => {
console.log('失败执行p2', reason);
})
p3.then(value => {
console.log('成功执行p3', value);
}, reason => {
console.log('失败执行p3', reason);
})
console.log(p)
console.log(p2)
// 返回状态是成功或者失败,值时[value]/[reason]
// Promise.resolve([value]) // 相当于创建个实例 状态是成功态 和下面的一样
// new Promise(resolve => {
// resolve([value])
// })
// Promise.reject([reason])
then 链细节小知识
-
如果状态一旦确定,想去执行.then注入的某个方法,但是此方法没有被注册,则向下顺延(找下一个then中注册的对应方法)
-
throw new Error('AAA')也可以报错
// 因为没有写成功态报的警告
Promise.reject(100).then(value => {
console.log('OK', value);
}
)
// 如果状态一旦确定,想去执行.then注入的某个方法,但是此方法没有被注册,则向下顺延(找下一个then中注册的对应方法)
Promise.reject(100).then(value => {
console.log('OK', value);
}/* 如果没有reason 但其实相当于默认加的这个 所以顺延到下一个then
reason=> {
return Promise.reject(reason)
}
*/
).then(null, reason => {
console.log('NO', reason); // NO 100
return Promise.resolve(200)
}).then(null, reason => {
console.log('NO', reason);
}).then(value => {
console.log('OK', value); // OK 200
})
.catch
- .catch (reason=> {}) === .then(null,reason=>{}) 等价其实就是两个写法
- Promise.prototype.catch()方法是Promise.prototype.then(undefined, onRejected)方法的别名,用于指定发生错误时的回调函数。
- catch方法接受一个参数,该参数是一个函数,拥有一个参数reason,参数的含义是Promise失败的原因
Promise.reject(100).then(value => {
console.log('OK', value);
}).catch(reason => {
console.log('NO', reason);
})
.finally
方法用于在Promise成功或拒绝时使用,不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数,它可以防止Promise的then()和catch()方法中的代码重复。
let p = new Promise((resolve,reject) => {
resolve('resolve')
})
p.then((res) => {
console.log(res)
}).catch((err) => {
console.log(err)
}).finally(()=> {
console.log('this finally')
})
使用场景
- 平常在工作中经常会遇到一些加载loading的需求,一般都在请求前设置loading展示,请求完成后关闭loading,刚开始我下意识把关闭loading放到then方法里面去处理,发现如果请求出现错误的情况,页面loading是还在的,就需要在catch方法里重写一遍导致代码重复,后面才了解到finally方法可以处理Promise的then和catch方法中代码重复的功能
this.opLoading = true;
api.then((res) => {
if(res.code == 200){
// xxx
}
}).catch((err) => {
// xxx
}).finally(()=> {
this.opLoading = false;
})
all
- all 等待所有promise实例都是成功,整体返回的实例才是成功(都成功整体才是成功,只要有一个失败,就是失败)
成功态例子
function fn1 () {
return Promise.resolve(1)
}
function fn2 () {
return new Promise((resolve => {
setTimeout(() => {
resolve(2)
}, 2000)
}))
}
function fn3 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(3)
}, 1000)
})
}
Promise.all([fn1(), fn2(), fn3()]).then(values => {
// values[Array]按顺序存储每一个实例返回的结果
console.log(values); // [1, 2, 3]
}).catch(reason => {
// 一旦有是失败的,整体都是失败,存储的是当前失败这个实例的原因
console.log('NO', reason);
})
失败的例子
function fn1 () {
return Promise.resolve(1)
}
function fn2 () {
return new Promise((resolve => {
setTimeout(() => {
resolve(2)
}, 2000)
}))
}
function fn3 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(3)
}, 1000)
})
}
Promise.all([fn1(), fn2(), fn3()]).then(values => {
console.log(values);
}).catch(reason => {
// 一旦有是失败的,整体都是失败,存储的是当前失败这个实例的原因
console.log('NO', reason); //NO 3
})
课外题# Promise.all哪怕一个请求失败了也能得到其余正确的请求结果的解决方案
race
- 等待最新有返回结果的promise实例,此实例的成功和失败决定最后的成功和失败(赛跑:谁快听谁的)
function fn1 () {
return Promise.resolve(1)
}
function fn2 () {
return new Promise((resolve => {
setTimeout(() => {
resolve(2)
}, 2000)
}))
}
function fn3 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(3)
}, 1000)
})
}
Promise.race([fn1(), fn2(), fn3()]).then(value => {
console.log('ok', value); //1
}).catch(reason => {
console.log('NO', reason);
})
如果 fn3 换成失败态 输出快的失败就是3
Promise.race([ fn2(), fn3()]).then(value => {
console.log('ok', value);
}).catch(reason => {
console.log('NO', reason); //3
})
async await
- async /await 是ES7中提供的,它是对promise的一个补充(promise的语法糖)
- 用async 修饰一个函数,函数返回的结果都会变成一个promise实例
- 状态: 大多都是成功的,如果代码执行报错,返回失败,再如果手动返回一个新的promise实例,则按照新实例的状态处理
- 用await可以把一个异步任务变为类似于同步的效(本质不是同步,还是异步,而且是异步中的微任务)
async function fn1 () {
//console.log(a);
return 10
// return Promise.reject('10') //手动返回实例
}
console.log(fn1()); // 加上async 返回一个promise实例
Promise {<fulfilled>: 10}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: 10
function fn1 () {
return 10
}
function fn2 () {
return new Promise((resolve => {
setTimeout(() => {
resolve(2)
}, 2000)
}))
}
function fn3 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(3)
}, 1000)
})
}
(async function () {
// 方法中想用await,必须把方法基于async修饰
// + 先把fn2执行,观察fn2返回的是成功还是失败的promise
// + 异步性:它会把当前上下文,await 下面的代码整体都当作一个异步的微任务,放置在EventQueue中
// + await 只是处理promise实例是成功状态的,如果返回状态是成功,则value拿到的是[[PromiseResult]],并且把之前存储的异步
// 任务拿到栈中让主线程把其执行
// await 处理后,立即把函数执行,哪怕函数立即返回成功或者失败的状态,await也没有把其立即处理,而是先等同步的都执行完,
// 再去处理执行这些异步的任务
let value = await fn2()
console.log(value);
console.log('@@');
})()
console.log(1);
(async function () {
try {
// 对于await 处理的时候,如果返回的是失败promise,则await下面代码不再执行,失败情况下没有处理浏览器抛出异常信息,
// 此时我们可以基于 try/catch捕获异常,从而进行异常的处理
let value = await fn3()
console.log(value);
console.log('@@');
} catch (e) { }
})()