了解promise之前需要先了解什么是异步编程
异步编程:
我们知道Javascript语言的执行环境是"单线程"。也就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务。但是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。因此出现了两种模式:同步和异步
异步任务就是不具有”堵塞“效应
Promise 是用来管理异步编程的,它本身不是异步的
Promise就是异步编程的解决方案之一
Promise本身是一个对象,是一个构造函数,它需要传入一个参数,并且这个参数必须是函数;传入的这个函数本身包含两个参数resolve
和reject,这两个参数也分别是函数;
new Promise((resolve,reject) => {
// 箭头函数
// 异步函数(定时器,事件和 ajax)
setTimeout( ()=> { });
});
Promise的三种状态
- 待定(pending)
- 兑现(fulfilled,有时也称为解决,resolve)
- 拒绝(rejected)
有两种过度:
pending -> fulfilled
或者是pending -> rejected
resolve.then、reject.catch
- then方法接受一个参数-resolve返回的数据(正常时),(遇到resolve函数会执行then)
- catch方法接收一个参数-reject返回的信息(抛出异常),(遇到reject函数汇之星catch)
new Promise((resolve,reject) => {
setTimeout(()=> {
//resolve('success')
reject('Error Data')
},1000)
}).then((data)=>{
console.log(data);
}).catch((data)=> {
console.log(data);
})
错误处理
Promise 对象的错误具有“冒泡”性质,会一直向后传递
补充:
1.使用
reject
reject('promise使用reject抛出异常')
2.使用
new Error()
let promise = new Promise((reslove, reject) => { throw new Error('promise使用Error抛出异常') }) promise().then(res => { console.log(res) }) .catch(err => { console.log(err.message) //'promise使用Error抛出异常' })
3.
reject
一个new Error()
let promise = new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('promise抛出异常')); }, 1000); }) promise.then(res => { console.log(res); }) .catch(err => { console.log(err.message); //'promise抛出异常' })
Promise的链式编程思想
- 每次你对Promise调用then,它都会创建并返回一个新的Promise,我们可以将其链接起来;
- 不管从then调用的完成回调(第一个参数)返回的值是什么,它都会被自动设置为被链接Promise(第一点中的)的完成。
let p1=new Promise((resolve,reject)=>{
resolve('这是p1--resolve') // 决定了下个then中成功方法会被执行
})
// 连接p1
let p2=p1.then(result=>{
console.log('成功1 '+result)
return Promise.reject('这是p2--reject')
// 返回一个新的Promise实例,决定了当前实例是失败的,所以决定下一个then中失败方法会被执行
},reason=>{
console.log('失败1 '+reason)
return 200
})
// 连接p2
let p3=p2.then(result=>{
console.log('成功2 '+result)
},reason=>{
console.log('失败2 '+reason)
})
// 成功1 这是p1--resolve
// 失败2 这是p2--reject
new Promise出来的实例,成功或者失败(或者异常),执行的是resolve或reject,
new Promise(resolve=>{
resolve(p) // 报错
// 这个executor函数执行发生异常错误,决定下个then失败方法会被执行
}).then(result=>{
console.log(`成功:${result}`)
return result*10
},reason=>{
console.log(`失败:${reason}`)
// 执行这句时候,没有发生异常或者返回一个失败的Promise实例,所以下个then成功方法会被执行
// 这里没有return,最后会返回 undefined
}).then(result=>{
console.log(`成功:${result}`)
},reason=>{
console.log(`失败:${reason}`)
})
VM1580:8 失败:ReferenceError: p is not defined
VM1580:12 成功:undefined
Promise {<fulfilled>: undefined}
Promise.all
let p = Promise.all([p1,p2,p3]);
p.then()
// (全部成功)
// 1.只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
// 2.只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,第一个被reject的实例的返回值,会传递给p的回调函数。
Promise.race
let p = Promise.race([p1,p2,p3]);
// (只要有一个成功)只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数
Promise.allSettled
//Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。
Promise.any
有一个变成fulfilled状态,包装实例就会变成fulfilled状态;
Promise.resolve
Promise.resolve(param):将对象转为 Promise 对象
Promise.reject
返回一个promise实例,且该实例的状态为rejected。
手写Promise
1)promise是通过构造函数创建的,并且会立即执行里面的构造函数
2)state表示promise的三种状态,三种状态中的一种:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected),最初为pending;当状态改变后,就不能再次更改状态,且终值也不可改变
3)使用value存放成功的值,reason存放失败原因
4)then的实现:无论成功还是失败都需要执行then函数,需要做的不过是根据状态判断去执行哪一个函数
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
var _this = this
this.state = PENDING; //状态
this.value = undefined; //成功结果
this.reason = undefined; //失败原因
function resolve(value) {
if(_this.state === PENDING){
_this.state = FULFILLED
_this.value = value
}
}
function reject(reason) {
if(_this.state === PENDING){
_this.state = REJECTED
_this.reason = reason
}
}
// 实例化时候立即执行构造函数,并传入resolve和reject
// 异常处理
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
if(this.state === FULFILLED){
typeof onFulfilled === 'function' && onFulfilled(this.value)
}
if(this.state === REJECTED){
typeof onRejected === 'function' && onRejected(this.reason)
}
};
module.exports = Promise;
5)then支持异步:参考发布订阅模式
先定义两个数组,分别用来存储成功的回调和失败的回调
function Promise(executor) {
this.onFulfilled = [];//成功的回调
this.onRejected = []; //失败的回调
}
如果当前还是PENDING状态,就把回调函数寄存到一个数组中,当状态发生改变时(resolve或者reject时),去数组中取出回调函数并执行
// then里面的state为pending时
if(this.state === PENDING){
typeof onFulfilled === 'function' && this.onFulfilled.push(onFulfilled)
typeof onRejected === 'function' && this.onRejected.push(onRejected)
}
// 状态改变时
function resolve(value) {
if(_this.state === PENDING){
_this.state = FULFILLED
_this.value = value
_this.onFulfilled.forEach(fn => fn(value))
}
}
function reject(reason) {
if(_this.state === PENDING){
_this.state = REJECTED
_this.reason = reason
_this.onRejected.forEach(fn => fn(reason))
}
}
综上所说,小结一下:
在我们new Promise的时候,executor就同步的执行了,根据executor里有无异步操作分一下两种情况
有异步操作:
- executor函数执行完(实例化完成)(此时内部还有个异步函数 如setTimout是异步的,需要等待几毫秒后执行)
- 拿到实例对象后,代码接着执行.then,此时很明显状态是pending(因为只执行了executor,参数是异步函数,此时未执行就 不会改变状态),我们会把用户在then里传的回调函数存起来
- 异步函数执行完成后,会遍历onFulfilled,并执行每一项(也就是then里面传过来的回调函数)
- 不管如何异步,在成功的地方调resolve,在失败的地方调reject,这是规矩,使用Promise必须遵守的规矩。
无异步操作:
- 执行executor直接就会调resolve,then的会后,任务已经完成,当即执行用户传的回调函数就行了
6)then链式调用
Promise.prototype.then = function (onFulfilled, onRejected) {
var _this = this
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
var promise2 = new Promise((resolve, reject)=>{
if(_this.state === FULFILLED){
let x = onFulfilled(_this.value)
promiseDeal(promise2, x, resolve, reject)
} else if(_this.state === REJECTED){
let x = onRejected(_this.reason)
promiseDeal(promise2, x ,resolve, reject)
} else if(_this.state === PENDING){
_this.onFulfilled.push(()=>{
let x = onFulfilled(_this.value)
promiseDeal(promise2, x, resolve, reject)
})
_this.onRejected.push(()=>{
let x = onRejected(_this.reason)
promiseDeal(promise2, x ,resolve, reject)
})
}
})
return promise2;
};
// 处理 onFulfilled或者onRejected 返回值x和新的promise 的函数
function promiseDeal(promise2, x, resolve, reject){
if(promise2 === x){
reject(new TypeError('Chaining cycle'))
}
if(x && typeof x === 'object' || typeof x === 'function'){
let used;
try {
let then = x.then
if(typeof then === 'function'){
then.call(x, (y)=>{
if (used) return;
used = true
promiseDeal(promise2, y, resolve, reject)
}, (r) =>{
if (used) return;
used = true
reject(r)
})
} else {
if (used) return;
used = true
resolve(x)
}
} catch(e){
if (used) return;
used = true
reject(e)
}
} else {
resolve(x)
}
}
注明:手写promise部分 转载于知乎账号 谢小飞 ,感谢感谢~ https://zhuanlan.zhihu.com/p/144058361