Promise是JavaScript中用于处理异步操作的一个关键概念。它代表了一个尚未完成但预期在将来完成的操作
1.1 基本用法
Promise是一个类,可以翻译成承诺、许诺 、期约
1、executor
Promise的参数是一个executor函数,会在promise被调用时,立即执行
而executor也有两个参数,分别是reoslve函数、reject函数
// 这个传入的函数,称为executor
const promise = new Promise(() => {
console.log("我会立刻被执行");
})
// executor有两个参数:reoslve、reject
const promise = new Promise((resolve, reject) => {
resolve() // 立刻执行promise.then()
reject() // 立刻执行promise.catch()
})
下面这几种使用方式都是类似的
const promise = new Promise((resolve, reject) => {})
//写法一
promise.then( res => {
console.log(res)
}, err => {
console.log(err)
})
//写法二
promise.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
resolve或reject传递参数时,如果参数也是一个promise,那么最终Promise的状态由这个参数promise决定
2、promise的三种状态
1、待定(pending):初始状态,既没有被兑现,也没有被拒绝(当执行executor中的代码时,处于该状态)
2、已兑现(fulfilled):意味着操作成功,执行了resolve时,处于该状态
3、已拒绝(rejected):意味着操作失败,执行了reject时,处于该状态
new Promise((resolve, reject) => {
console.log('executor')
//resolve()
//reject()
}).then(res => {
console.log(res)
}, err => {
console.log(err)
})
状态一旦确定,便不可更改,但不意味着后面的其他代码不能被执行
3、resolve的参数
1、参数是普通的值,则直接传递
new Promise((resolve, reject) => {
resolve("参数")
}).then(res => {
console.log(res)
})
//输出:参数
2、参数是一个Promise
如果参数是一个promise,那么当前promise的状态会由传入的promise来决定
const inSidePromise = new Promise((resolve, reject) => {
resolve("我是inSidePromise")
})
new Promise((resolve, reject) => {
resolve(inSidePromise)
}).then(res => {
console.log(res)
})
//输出:我是inSidePromise
3、thenable
如果传入一个对象,并且这个对象有实现then方法,那么也会执行then方法,并且由该then方法决定后续状态
new Promise((resolve, reject) => {
const obj = {
then: function(resolve, reject) {
reject("reject message")
}
}
resolve(obj)
}).then(res => {
console.log('res:', res)
}, err => {
console.log('err:', err)
})
//本来应该输出obj对象
//结果输出:err: reject message
1.2 promise的对象方法
promise的对象方法是放在Promise的原型上的,我们可以打印一下上面都有什么方法
Promise.prototype
//输出:Object [Promise] {}
//遍历所有属性描述器
Object.getOwnPropertyDescriptors(Promise.prototype)
//输出:constructor、then、catch、finally等
1、then方法
特点1:同一个promise可以被多次调用then方法
当我们的resolve方法被回调时,所有then方法传入的回调函数都会被调用
const promise = new Promise((resolve, reject) => {
resolve("linlinlin~")
})
promise.then(res => {
console.log('res1:', res)
})
promise.then(res => {
console.log('res2:', res)
})
promise.then(res => {
console.log('res3:', res)
})
//输出:
//res1: linlinlin~
//res2: linlinlin~
//res3: linlinlin~
特点2:then方法传入的“回调函数”,可以有返回值
如果我们返回一个普通值,那么这个普通值会被作为一个新的promise的resolve值
promise.then(res => {
return 'abcdefg'
})
//相当于
promise.then(res => {
return new Promise(resolve => {
resolve('abcdefg')
})
})
所以,then方法本身也有返回值,它返回了一个promise
const promise = new Promise((resolve, reject) => {
resolve('1111111')
})
promise.then(res1 => {
console.log(res1);
return '2222222'
}).then(res2 => {
console.log(res2);
})
//输出:1111111 2222222
补充:
如果返回的是一个promise,里边还是会使用一个Promise包裹return的promise,但是最终状态由里边的promise决定;
如果返回的是对象,并且对象实现了thenable,会执行其中的then方法,并且由该then方法决定后续状态
2、catch方法
利用then的第二个参数捕获错误
const promise = new Promise((resolve, reject) => {
reject('rejected status')
})
promise.then(undefined, err => {
console.log("err:", err)
})
即使不使用reject,当executor抛出异常时,也是会调用错误捕获的回调函数的
const promise = new Promise((resolve, reject) => {
throw new Error("rejected status")
})
promise.then(undefined, err => {
console.log("err:", err) //触发
})
但是利用then方法的第二个参数捕获异常,会使得代码十分臃肿。所以更推荐使用catch方法捕获异常
const promise = new Promise((resolve, reject) => {
reject('rejected status')
})
promise.catch(err => {
console.log("err:", err)
})
思考:下方的链式调用中,catch捕获的是谁的异常
const promise = new Promise((resolve, reject) => {
reject('rejected status')
})
promise.then(res => {
return new Promise((resolve, reject) => {
reject("then rejected status")
})
}).catch (err => {
console.log("谁的err", err)
})
//输出:谁的err rejected status
//上面的代码中有两个promise,err默认先捕获了外层的错误
3、finally方法
表示无论Promise对象无论变成fulfilled还是reject状态,最终都会被执行的代码
const promise = new Promise((resolve, reject) => {
reject("reject")
})
promise.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
}).finally(() => {
console.log("finally action")
})
1.3 promise的类方法
前面学习的then、catch、finally方法都属于Promise的实例方法,都是存放在Promise的prototype上。还有直接存放在Promise类上的方法
1、resolve方法
思考:如何将一个对象转换成promise对象,传给其他人调用
//一般方法
function foo() {
let obj = {
name: 'xiaoming'
}
return new Promise(resolve => {
resolve(obj)
})
}
//调用
foo().then(res => {
console.log(res)
})
使用Promise.resolve()方法也可以
const promise = Promise.resolve({name: 'xiaoming'})
promise.then(res => {
console.log('res:', res)
})
2、reject方法
reject方法与resolve方法类似
const promise = Promise.reject("rejected message")
//相当于
const promise2 = new Promise((resolve, reject) => {
reject("rejected message2")
})
3、all方法
promise.all方法,可以将多个Promise包裹在一起形成一个新的Promise
新的promise状态由参数共同决定(所有参数状态为fulfilled,返回一个结果数组;但是只要有一个参数状态为reject,返回reject)
const p1 = new Promise((resolve, reject) => {
resolve('1111')
})
const p2 = new Promise((resolve, reject) => {
resolve('2222')
})
Promise.all([p1, p2]).then(res => {
console.log(res)
})
//输出:[ '1111', '2222' ]
const p1 = new Promise((resolve, reject) => {
reject('error') //reject
})
const p2 = new Promise((resolve, reject) => {
resolve('2222')
})
Promise.all([p1, p2]).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
//输出:error
4、race方法
只要有一个promise状态发生了改变,就停止执行返回结果
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("1111");
}, 1000); //1s
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("2222");
}, 2000); //2s
});
Promise.race([p1, p2])
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
//输出:1111
1.4 promise的实现
1、基本结构
1、Promise接收一个参数(executor函数),并且创建实例时,会直接执行executor函数
class MzPromise {
constructor(executor) {
executor()
}
}
const promise = new MzPromise(() => {
console.log("传进来的函数被执行了")
})
//输出:传进来的函数被执行了
2、加入resolve,reject函数
executor函数,接收两个函数作为参数
class MzPromise {
constructor(executor) {
const resolve = () => {
console.log("resolve被调用了")
}
const reject = () => {
console.log("reject被调用了")
}
executor(resolve, reject)
}
}
const promise = new MzPromise((resolve, reject) => {
resolve()
reject()
})
//输出:resolve被调用了、reject被调用了
问题:resolve、reject不能同时被调用。要么状态是fulfilled、要么是rejected
3、加入三种状态
const PROMISE_STATUS_PENDDING = "pending";
const PROMISE_STATUS_FULFILED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";
class MzPromise {
constructor(executor) {
//默认状态是pending
this.status = PROMISE_STATUS_PENDDING;
const resolve = () => {
if (this.status === PROMISE_STATUS_PENDDING) {
//改变状态
this.status = PROMISE_STATUS_FULFILED;
console.log("resolve被调用了");
}
};
const reject = () => {
if (this.status === PROMISE_STATUS_PENDDING) {
//改变状态
this.status = PROMISE_STATUS_REJECTED;
console.log("reject被调用了");
}
};
executor(resolve, reject);
}
}
//测试
const promise = new MzPromise((resolve, reject) => {
resolve()
reject()
})
//输出:resolve被调用了
率先执行了resolve函数,状态变为满足状态,之后的reject便不会执行了
4、在resolve、reject中传入参数
class MzPromise {
constructor(executor) {
//默认状态是pending
this.status = PROMISE_STATUS_PEDDING
this.value = undefined
this.reason = undefined
const resolve = (value) => {
if(this.status === PROMISE_STATUS_PEDDING) {
this.status = PROMISE_STATUS_FULFILED
this.value = value //保存传递来的value
console.log("resolve被调用了")
}
}
const reject = (reason) => {
if(this.status === PROMISE_STATUS_PEDDING) {
this.status = PROMISE_STATUS_REJECTED
this.reason = reason //保存传递来的value
console.log("reject被调用了")
}
}
executor(resolve, reject)
}
}
2、对象方法then的实现
我们知道,当Promise执行resolve()时,会调用then方法
new Promies(resolve => {
resolve() //执行
}).then(res => {
}, err => {
})
then方法的回调在哪里调用呢?
class MzPromise {
constructor(executor) {
const resolve = (value) => {
if(this.status === PROMISE_STATUS_PEDDING) {
this.status = PROMISE_STATUS_FULFILED
this.value = value //保存传递来的value
console.log("resolve被调用了")
then传进来的回调函数1
}
}
const reject = (reason) => {
if(this.status === PROMISE_STATUS_PEDDING) {
this.status = PROMISE_STATUS_REJECTED
this.reason = reason //保存传递来的value
console.log("reject被调用了")
then传进来的回调函数2
}
}
executor(resolve, reject)
}
}
实现then方法,并在相应的resolve或者reject中调用
class MzPromise {
constructor(executor) {
const resolve = (value) => {
if(this.status === PROMISE_STATUS_PEDDING) {
this.status = PROMISE_STATUS_FULFILED
this.value = value
console.log("resolve被调用了")
//then传进来的回调函数1
this.onFulfilled() //调用
}
}
executor(resolve, reject)
}
//then方法-MzPromise的对象方法
then(onFulfilled, onReject) {
this.onFulfilled = onFulfilled //保存传来的onFulfilled函数
this.onReject = onReject //保存传来的onReject函数
}
}
但是执行时,报错了。主要是this.onFulfilled()的执行时机有问题。
当我们创建实例时,执行了resolve()函数,此时会调用MzPromise中的resolve的方法,执行到this.onFulfilled()行时,找不到该方法,直接就报错了。
解决办法就是将MzPromise中的resolve的方法延迟执行,直到then方法已被执行
//使用setTimeout延迟
class MzPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PEDDING
const resolve = (value) => {
if(this.status === PROMISE_STATUS_PEDDING) {
this.status = PROMISE_STATUS_FULFILED
setTimeout(() => {
this.value = value //保存传递来的value
this.onFulfilled(this.value)
}, 0)
}
}
const reject = (reason) => {
if(this.status === PROMISE_STATUS_PEDDING) {
this.status = PROMISE_STATUS_REJECTED
setTimeout(() => {
this.reason = reason //保存传递来的value
this.onReject(this.reason)
}, 0)
}
}
executor(resolve, reject)
}
// then 方法
then(onFulfilled, onReject) {
this.onFulfilled = onFulfilled
this.onReject = onReject
}
}
const promise = new MzPromise((resolve, reject) => {
resolve("我是resolve")
reject("我是reject")
})
promise.then(res => {
console.log("res:", res)
}, err => {
console.log( "err:", err);
})
//输出:res: 我是resolve
上方的代码,基本实现了then方法。但是Promise是微任务队列的任务,而setTimeout属于宏任务,使用setTimeout显然不合适
应该利用一个能将代码添加进微任务,又能延迟执行的方法——queueMicrotask
class MzPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PEDDING
const resolve = (value) => {
if(this.status === PROMISE_STATUS_PEDDING) {
this.status = PROMISE_STATUS_FULFILED
queueMicrotask(() => {
this.value = value //保存传递来的value
this.onFulfilled(this.value)
})
}
}
executor(resolve, reject)
}
}
3、then方法的优化
问题1:上边的then方法还存在着许多缺陷:比如不能多次调用
解决办法:分别将then方法中resolve、reject回调放进数组中,调用时直接遍历出数组中的函数并进行调用
class MzPromise {
constructor(executor) {
this.onFulfilledFns = []
this.onRejectFns = []
const resolve = (value) => {
if(this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILED
queueMicrotask(() => {
console.log('resolve被调用了');
this.value = value
this.onFulfilledFns.forEach(item => {
item(this.value)
})
})
}
}
executor(resolve, reject)
}
then(onFulfilled, onReject) {
// 将成功和失败的回调放进数组中
this.onFulfilledFns.push(onFulfilled)
this.onRejectFns.push(onReject)
}
}
这样一来就可以多次调用了
promise.then(res => {
console.log('res:', res);
}, err => {
console.log('err:', err);
})
promise.then(res => {
console.log('res1:', res);
}, err => {
console.log('err2:', err);
})
问题2:在确定Promise状态后,再次调用then将不会执行
setTimeout(() => {
promise.then(res => {
console.log('res3:', res);
}, err => {
console.log('err3:', err);
})
}, 1000)
//输出:无输出
解决
class MzPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectFns = []
const resolve = (value) => {
if(this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if(this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
console.log('resolve被调用了');
this.value = value
this.onFulfilledFns.forEach(item => {
item(this.value)
})
})
}
}
executor(resolve, reject)
}
then(onFulfilled, onReject) {
if(this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
onFulfilled(this.value)
}
if(this.status === PROMISE_STATUS_REJECTED && onReject) {
onReject(this.reason)
}
// 将成功和失败的回调放进数组中
this.onFulfilledFns.push(onFulfilled)
this.onRejectFns.push(onReject)
}
}
下一步优化要实现then的链式调用,暂略
目前阶段完整代码
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class MzPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectFns = []
const resolve = (value) => {
if(this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if(this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
console.log('resolve被调用了');
this.value = value
this.onFulfilledFns.forEach(item => {
item(this.value)
})
})
}
}
const reject = (reason) => {
if(this.status === PROMISE_STATUS_PENDING) {
// 添加微任务
queueMicrotask(() => {
if(this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
console.log("reject被调用了");
this.reason = reason
this.onRejectFns.forEach(item => {
item(this.reason)
})
})
}
}
executor(resolve, reject)
}
then(onFulfilled, onReject) {
if(this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
onFulfilled(this.value)
}
if(this.status === PROMISE_STATUS_REJECTED && onReject) {
onReject(this.reason)
}
// 将成功和失败的回调放进数组中
this.onFulfilledFns.push(onFulfilled)
this.onRejectFns.push(onReject)
}
}
const promise = new MzPromise((resolve, reject) => {
console.log("executor");
reject('err')
// resolve('res')
})
promise.then(res => {
console.log('res:', res);
}, err => {
console.log('err:', err);
})
4、类方法的实现
1、实现resolve方法
class MzPromise {
static resolve(value) {
return new MzPromise((resolve) => resolve(value))
}
}
//创建实例
MzPromise.resolve("hello woeld") //把该字符串转成了promise
.then(res => {
concole.log('res:', res)
})
//输出:res:hello world
2、实现reject方法
class MzPromise {
static reject(reason) {
return new MzPromise((resolve, reject) => reject(reason))
}
}
//创建实例
MzPromise.reject("error").then(undefined, err => {
console.log('err:', err);
})
//输出:err: error
3、实现all方法
问题关键:什么时候执行resolve,什么时候要执行reject
class MzPromise {
static all(promises) {
return new MzPromise((resolve, reject) => {
const values = []
promises.forEach(promise => {
promise.then(res => {
values.push(res)
if(values.length === promises.length) {
resolve(values)
}
}, err => {
reject(err)
})
})
})
}
}
//测试代码
const p1 = new Promise((resolve, reject) => {
resolve('10')
})
const p2 = new Promise((resolve, reject) => {
resolve("20")
})
const p3 = new Promise((resolve, reject) => {
resolve('30')
})
MzPromise.all([p1, p2, p3]).then(res => {
console.log('res:', res);
}, err => {
console.log('err:', err)
})
//输出:res: [ '10', '20', '30' ]
4、实现race方法
static race(promises) {
return new MzPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(res => {
//只要一有结果,立刻resolve
resolve(res)
}, err => {
reject(err)
})
})
})
}
//测试代码
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('10')
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('20')
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('30')
}, 3000)
})
MzPromise.race([p1, p2, p3]).then(res => {
console.log('res:', res);
}, err => {
console.log('err:', err)
})
//输出:res:10
