深入了解Promise,掌握Promise基础使用、链式调用、静态方法等;通过手写Promise深入理解其核心机制
Promise简介
Promise 是异步编程的一种解决方案: 从语法上讲,
promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态) ;状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
promise解决的问题:
- 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
promise可以解决异步的问题,但不能说promise是异步的(注意promise本身是同步的)
Promise的基本使用
由下面可知:then,catch,finally都是Promise的实例方法,放在Promise.prototype上。
console.log(Object.getOwnPropertyDescriptors(Promise.prototype));
// {
// constructor: {
// value: [Function: Promise],
// writable: true,
// enumerable: false,
// configurable: true
// },
// then: {
// value: [Function: then],
// writable: true,
// enumerable: false,
// configurable: true
// },
// catch: {
// value: [Function: catch],
// writable: true,
// enumerable: false,
// configurable: true
// },
// finally: {
// value: [Function: finally],
// writable: true,
// enumerable: false,
// configurable: true
// },
// [Symbol(Symbol.toStringTag)]: {
// value: 'Promise',
// writable: false,
// enumerable: false,
// configurable: true
// }
// }
创建
使用 new Promise 构造函数可以创建一个 Promise 对象,并提供一个执行器函数(executor function),该函数有两个参数,通常命名为 resolve 和 reject:
const myPromise = new Promise((resolve, reject) => {
// 异步操作
if (/* 操作成功 */) {
resolve('Promise is resolved successfully.');
} else {
reject('Promise is rejected with an error.');
}
});
使用
then() 和 catch() 方法
.then(onFulfilled, onRejected):当 Promise 被成功解决时,调用 onFulfilled 回调;如果被拒绝,则调用 onRejected 回调。.catch(onRejected):用于指定当 Promise 被拒绝时的回调函数。
myPromise
.then((value) => {
console.log(value); // "Promise is resolved successfully."
})
.catch((error) => {
console.error(error);
});
或者:
myPromise
.then((value) => {
console.log(value); // "Promise is resolved successfully."
}, catch((error) => {
console.error(error);
}))
finally() 方法
.finally(onFinally):无论 Promise 对象是被解决还是被拒绝,都会执行的回调函数。
myPromise
.then((value) => console.log(value))
.catch((error) => console.error(error))
.finally(() => console.log('Promise is settled.'));
链式调用
Promise 的 then() 和 catch() 方法返回的都是一个新的 Promise 对象,这允许进行链式调用。
myPromise
.then((firstResult) => {
// 处理第一个异步操作的结果
return anotherAsyncOperation();
})
.then((secondResult) => {
// 处理第二个异步操作的结果
})
.catch((error) => {
// 处理错误
});
Promise状态
三种状态
一个 Promise 对象有三种状态:
- Pending(进行中) :初始状态,既不是成功,也不是失败状态。
- Fulfilled(已成功) :操作成功完成。
- Rejected(已失败) :操作失败。
Promise 的状态转移是单向的,只能从 Pending 到 Fulfilled 或 Pending 到 Rejected,不能逆向转换,也不能从成功或失败状态转回进行中状态。
resolve()的参数决定promise状态
当 Promise 的 resolve 方法被调用时,其参数决定了 Promise 的状态:
- 如果参数是一个普通值或普通对象(非 Promise 对象) ,则 Promise 对象变为
fulfilled(已解决)状态,并且该值成为 Promise 的结果值。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success') // 传入的是一个普通值,则promise的状态会变为fulfilled
}, 1000)
})
promise.then((data) => {console.log(data);}) // success
-
如果参数是一个 Promise 对象,则原始 Promise 对象的状态取决于被解析的 Promise 对象的状态:
- 如果被解析的 Promise 对象是
pending状态,则原始 Promise 对象也保持pending状态,直到被解析的 Promise 对象状态改变。 - 如果被解析的 Promise 对象是
fulfilled(已解决)状态,则原始 Promise 对象也变为fulfilled状态,并且其值为被解析的 Promise 对象的值。 - 如果被解析的 Promise 对象是
rejected(已拒绝)状态,则原始 Promise 对象也变为rejected状态,并且其原因为被解析的 Promise 对象的原因。
- 如果被解析的 Promise 对象是
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(new Promise((resolve, reject) =>{
reject("现在状态是rejected")
// 传入的是一个promise,此时的状态就会由resolve参数promise的状态决定,也就是rejected
}))
}, 1000)
})
promise.then((data) => {console.log(data);})
.catch((error) => {console.log(error);}) // 现在状态是rejected
-
如果参数是一个 thenable 对象(具有
then方法的对象) ,其行为类似于一个 Promise 对象:- 如果 thenable 对象的
then方法被成功调用,则原始 Promise 对象也变为fulfilled状态,并且其值为 thenable 对象resolve后的值。 - 如果 thenable 对象的
then方法抛出异常,则原始 Promise 对象也变为rejected状态,并且其原因为 thenable 对象抛出的异常。
- 如果 thenable 对象的
const thenableObj = {
then(resolve, reject){
resolve('我是thenable对象')
}
}
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(thenableObj) // 传入的是一个thenable对象,状态就由thenable对象决定
}, 1000)
})
promise.then((data) => {console.log(data);})
.catch((error) => {console.log(error);}) // 我是thenable对象
then方法
多次调用then方法
一个promise可以多次调用then方法。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
// 多次调用then方法
promise.then((data) => {console.log(data);})
promise.then((data) => {console.log(data);})
promise.then((data) => {console.log(data);})
// 输出:
// success
// success
// success
then方法的返回值(链式调用)
和
resolve()的参数决定promise状态类似。then方法的返回值都会作为执行完then方法后新产生的promise的resolve的参数。 默认不写的话就等同于return undefined会把undefined作为resolve的参数。
- 返回一个普通值(数值/字符串/普通对象/
undefined),那么这个返回的普通值将会作为新promise中resolve的参数。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
promise.then((data1) => {
console.log(data1)
return data1 // 会将data1传递给新Promise的resolve
})
.then(data2 => {console.log(data2)})
// 输出:
// success
// success
- 返回一个
promise,则新 Promise 的状态取决于被返回的 Promise 的状态。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
promise.then((data1) => {
console.log(data1)
return new Promise((resolve,reject) => {
setTimeout(() => {
reject('failure')
}, 1000)
}) // 新Promise的状态会由这个new Promise的状态决定
})
.then(data2 => {console.log(data2)})
.catch((error) => {console.log(error)})
// 输出:
// success
// failure
- 返回一个 thenable 对象(具有
then方法的对象),其行为类似于一个 Promise 对象:- 如果 thenable 对象的
then方法被成功调用,则原始 Promise 对象也变为fulfilled状态,并且其值为 thenable 对象resolve后的值。 - 如果 thenable 对象的
then方法抛出异常,则原始 Promise 对象也变为rejected状态,并且其原因为 thenable 对象抛出的异常。
- 如果 thenable 对象的
const thenableObj = {
then(resolve, reject){
resolve('我是thenable对象')
}
}
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
promise.then((data1) => {
console.log(data1)
return thenableObj // 新Promise的状态会由这个thenable对象决定
})
.then(data2 => {console.log(data2)})
.catch((error) => {console.log(error)})
// 输出:
// success
// 我是thenable对象
catch方法
catch方法不仅可以捕获reject,也可以捕获throw new Error()
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
// Promise A+的写法:
promise.then(success => console.log(success), error => console.log(error))
// ES6的写法(链式):
promise.then(success => console.log(success)).catch(error => console.log(error))
- 不能分开分别调用
then()和catch(),要么写成Promise A+的形式,要么写成ES6的形式。但是和then一样,catch也可以进行多次调用。 catch()按照顺序的优先级来捕获reject或throw new Error()
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
promise.then(success => {
console.log(success)
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('new Promise error')
}, 1000)
})
})
.catch(error => console.log(error))
// 输出:
// success
// new Promise error
上面代码中,最开始的promise是resolve,不会捕获异常,而then返回的是一个promise,其reject,故catch会捕获到reject('new Promise error')。
但下面这个例子中,最开始的promise是reject,会被catch捕获到,那么后面then返回的promise,虽然是reject,但是不会被捕获到。
catch就是按照这样的顺序优先级来捕获的。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
reject('failure') // 一开始就reject,则会捕获这的reject
}, 1000)
})
promise.then(success => {
console.log(success)
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('new Promise error')
}, 1000)
})
})
.catch(error => console.log(error))
// 输出:
// failure
catch和then一样,也可以有返回值,而且和then一样,返回的值会被传到new Promise的resolve。
finally方法
- 不管
resolve还是reject最终都会执行的。 - 回调函数没有参数。
finally也可以多次调用。
Promise的类方法(静态方法)
Promise.resolve()与Promise.reject()方法
用于返回成Promise。
Promise.resolve()
还是由resolve()的参数决定promise状态
// 传普通值(普通对象)
const promise1 = Promise.resolve({a:1, b:2})
promise1.then(data1 => console.log(data1))
// 相当于:
const promise1 = new Promise((resolve, reject) => {
resolve({a:1, b:2})
})
promise1.then(data1 => console.log(data1))
// 传一个promise
const promise2 = Promise.resolve(new Promise((resolve, reject) => resolve("hello, I'm a promise")))
promise2.then(data2 => console.log(data2))
// 相当于:
const promise2 = new Promise((resolve, reject) => {
resolve(new Promise((resolve, reject) => resolve("hello, I'm a promise")))
})
promise2.then(data2 => console.log(data2))
// 传一个thenable对象
const thenableObj = {
then(resolve, reject){
resolve('我是thenable对象')
}
}
const promise3 = Promise.resolve(thenableObj)
promise3.then(data3 => console.log(data3))
// 相当于:
const promise3 = new Promise((resolve, reject) => {
resolve(thenableObj)
})
promise3.then(data3 => console.log(data3))
考考你,下面这段代码会输出什么?
Promise.resolve()
.then(() => {
console.log(0);
return Promise.resolve(4);
})
.then((res) => {
console.log(res)
})
Promise.resolve()
.then(() => {
console.log(1);
})
.then(() => {
console.log(2);
})
.then(() => {
console.log(3);
})
.then(() => {
console.log(5);
})
.then(() => {
console.log(6);
})
// 输出:0 1 2 3 4 5 6
分析:
- 一个promise
resolve后,状态变成Fulfilled,会把then()里面的回调函数放到微队列里面执行;相对应的,reject后,状态变成Rejected,会把catch()里面的回调函数放到微队列里面执行。 then()方法会返回一个promise。- 如果promise1
resolve(普通值a)后在then()中return了一个新的promise(promise2),(V8源码中)那么就会把promise2.then((普通值a)=> promise1的then()返回的promise .resolve(普通值a))这一段代码放到微队列里面等待执行。
Promise.reject()
注意:
reject可不会和resolve一样由参数来决定promise状态,不管传的参数是什么都只会执行catch。
const thenableObj = {
then(resolve, reject){
resolve('我是thenable对象')
}
}
const promise = Promise.reject(thenableObj)
promise.then(data => console.log(data))
.catch((error) => {console.log(error)})
// 输出:{ then: [Function: then] }
Promise.all()和Promise.allSettled()
Promise.all()
- 当所有的promise都
resolve后会以数组的形式返回所有resolve的结果,顺序是按照写在Promise.all()里面的顺序,而不是按照每个promise响应的顺序。
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是promise1')
}, 1000)
})
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是promise2')
}, 2000)
})
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是promise3')
}, 3000)
})
Promise.all([promise3, promise2, promise1]).then(res => {
console.log(res);
})
// 输出:[ '我是promise3', '我是promise2', '我是promise1' ]
- 只要这些promise中有一个
reject了,那么就会直接catch出reject的那个promise( 只会catch出最先reject的那个,一遇到reject就会立即catch)。
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是promise1')
}, 1000)
})
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('我是promise2,reject了')
}, 2000)
})
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是promise3')
}, 3000)
})
Promise.all([promise3, promise2, promise1]).then(res => {
console.log(res);
})
.catch(err => console.log(err));
// 输出:我是promise2,reject了
Promise.allSettled()
Promise.allSettled()则会等所有promise都响应完最后直接输出一个 对象数组,里面包含每个promise的status和value/reason , 顺序还是按照写在Promise.allSettled()里面的顺序。
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是promise1')
}, 1000)
})
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('我是promise2,reject了')
}, 2000)
})
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是promise3')
}, 3000)
})
Promise.allSettled([promise3, promise2, promise1]).then(res => {
console.log(res);
})
.catch(err => console.log(err));
// 输出:
// [
// { status: 'fulfilled', value: '我是promise3' },
// { status: 'rejected', reason: '我是promise2,reject了' },
// { status: 'fulfilled', value: '我是promise1' }
// ]
Promise.race()和Promise.any()
Promise.race()
会调用第一个响应完的promise的
then()或catch()。
- 只要有一个Promise变成
fulfilled状态,那么就结束,执行相应的then() - 只要有一个Promise变成
rejected状态,那么就结束,执行相应的catch()
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是promise1')
}, 1000)
})
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('我是promise2,reject了')
}, 500)
})
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是promise3')
}, 3000)
})
Promise.race([promise3, promise2, promise1]).then(res => {
console.log(res);
})
.catch(err => console.log(err));
// 输出:我是promise2,reject了
Promise.any()
总是调用第一个
resolve的promise的then()。
Promise.any()方法会等到一个fulfilled状态,才会决定新Promise的状态- 如果所有的Promise都是
reject的,那么也会等到所有的Promise都变成rejected状态。
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('我是promise1,reject了')
}, 1000)
})
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('我是promise2,reject了')
}, 500)
})
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('我是promise3,reject了')
}, 3000)
})
Promise.any([promise3, promise2, promise1]).then(res => {
console.log(res);
})
.catch(err => console.error(err));
// 输出:
// [AggregateError: All promises were rejected] {
// [errors]: [ '我是promise3,reject了', '我是promise2,reject了', '我是promise1,reject了' ]
// }
Promise A+ 规范与ES6的promise
- ES6(ECMAScript 2015)中的
Promise是语言规范的一部分,并且现代浏览器和 JavaScript 运行环境都原生支持它。 - ES6 的
Promise是 JavaScript 异步编程的一个基础构件,它提供了一种更合理、更强大的异步编程模型。而Promise A+规范是Promise的行为标准,确保了不同 JavaScript 环境中Promise的一致性和可预测性。 - 在实际开发中,你通常不需要自己实现
Promise,而是直接使用 ES6 提供的Promise对象,或者使用库和框架提供的符合Promise A+规范的Promise实现。
ES6 Promise 的基本用法
ES6 中的
Promise是一个构造函数,是对Promise A+ 规范的实现。注意:构造函数本身不是Promise,而是其创建的对象是Promise。ES6 中的Promise还增加了catch(),finally(),Promise.all(),Promise.allSettled(),Promise.race(),Promise.any()
创建 Promise
const myPromise = new Promise((resolve, reject) => {
// 异步操作
const condition = true; // 假设这是异步操作的结果
if (condition) {
resolve('Promise is resolved successfully.');
} else {
reject('Promise is rejected with an error.');
}
});
使用 Promise
myPromise
.then((value) => {
console.log(value); // 如果 promise 被成功解决,这里会打印 value
})
.catch((error) => {
console.error(error); // 如果 promise 被拒绝,这里会打印 error
});
Promise A+ 规范
Promise A+是Promise的一个规范,早于ES6,它定义了Promise的行为和接口,它的出现是为了解决回调地狱的问题。ES6 中的Promise实现遵循了这个规范。Promise A+规范确保了不同 JavaScript 环境中Promise的一致性。
Promise A+ 规范的关键点
- 状态:
Promise对象有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。 - thenable 对象:遵循
Promise A+规范的Promise对象被称为thenable对象,因为它们拥有then方法。 - 链式调用:
Promise的then方法返回一个新的Promise对象,这允许进行链式调用。 - 错误处理:如果在
.then()或.catch()方法中抛出错误,那么这个错误会被下一个.catch()方法捕获。 - 回调函数:
Promise的构造函数接受的执行器函数(executor function)中,resolve用于解决Promise,reject用于拒绝Promise。 - 惰性执行:
Promise中的异步操作是惰性执行的,即直到Promise的.then()或.catch()方法被调用时才开始执行。
Promise 与 Promise A+ 的关系
- Promise A+ 是规范:它定义了
Promise应该如何工作,包括它的接口和行为。 - ES6 Promise 是实现:ES6 中的
Promise是对Promise A+规范的一个具体实现。 - 兼容性:遵循
Promise A+规范的任何Promise实现都应该与遵循相同规范的其他Promise实现兼容。
手写promise
手写Promise的工具函数
catch:
Promise.prototype.catch = function (onRejected) {
return this.then(null, onRejected)
}
catch 方法(Promise A+规范里面没有catch())实际上是通过调用 then 方法来实现的。具体地,它传递了 null 作为第一个参数(表示对于解决的情况不做处理),并将传入的 onRejected 回调函数作为第二个参数(用于处理拒绝的情况)。因此,当 Promise 被拒绝时,onRejected 回调函数将被调用,而对于解决的情况,则不做处理。
finally:
finally(onFinally){
return this.then(data =>{
onFinally()
// this的promise是成功的,那么返回的promise也是成功的
return data
}), err => {
onFinally()
// this的promise是失败的,那么返回的promise也是失败的
throw err
}
}
resolve:
function isPromiseLike(obj){
// 满足A+规范的对象:有then方法的对象
return obj && obj.then === 'function'
}
Promise.resolve = function(value){
// 如果value是一个Promise对象,直接返回
if(value instanceof Promise) return value
// 如果value是一个thenable对象,返回一个新的Promise对象,并且将value的状态传递给新的Promise对象
if(isPromiseLike(value)){
return new Promise((resolve, reject) => {value.then(resolve, reject)})
}
// 如果value是一个普通值,返回一个新的Promise对象,状态为fulfilled
return new Promise((resolve, reject) => {resolve(value)})
}
reject:
// Promise.reject(reason)返回一个状态为rejected的Promise对象
Promise.reject = function(reason){
return new Promise((resolve, reject) => {reject(reason)})
}
手写Promise(完整版)
// 用常量表示状态,防止硬编码
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
// 用#表示私有属性
// 定义状态state和传的值result
#state = PENDING
#result = undefined
// 定义回调函数(then)数组
#handlers = []
// 定义改变状态的方法
#changeState(state, result){
if(this.#state !== PENDING) return // Promise状态只能改变一次
this.#state = state
this.#result = result
// 状态改变后才执行,防止异步
this.#run()
}
// 判断是否是Promise对象
#isPromiseLike(value){
// 判断是否是对象并且有then方法
return value && typeof value.then === 'function'
}
// 将函数放到微任务队列中执行
#runMicroTask(func){
// 分环境:
// node环境
if(typeof process === 'object' && typeof process.nextTick === 'function'){
process.nextTick(func)
} else if(typeof MutationObserver === 'function'){
// 浏览器环墧:通过监听文本节点变化将函数放到微任务队列中
const observer = new MutationObserver(func)
const textNode = document.createTextNode('')
observer.observe(textNode, {characterData: true})
textNode.data = 'test'
} else {
// 其他环境:通过setTimeout将函数放到微任务队列中
setTimeout(func, 0);
}
}
// 执行回调函数(then)
// 状态确定后执行callback
#runOne(callback, resolve, reject){
this.#runMicroTask(() => {
if(typeof callback !== 'function'){
// 如果callback不是函数,根据状态执行resolve或reject
const settled = this.#state === FULFILLED ? resolve : reject
settled(this.#result)
return
}
// 如果callback是函数,执行callback
// 同时也要try catch,callback执行出错时执行reject
try{
const data = callback(this.#result)
// 如果callback返回的是Promise对象,执行then方法
if(this.#isPromiseLike(data)){
data.then(resolve, reject)
} else {
resolve(data)
}
} catch (err) {
reject(err)
}
})
}
// 执行回调函数(then)数组
#run(){
if(this.#state === PENDING) return
// console.log(this.#handlers);
while(this.#handlers.length){
// 每从handlers中取出一个handler,就解构出onFulfilled, onRejected, resolve, reject
const {onFulfilled, onRejected, resolve, reject} = this.#handlers.shift()
if(this.#state === FULFILLED){
this.#runOne(onFulfilled, resolve, reject)
} else if(this.#state === REJECTED){
this.#runOne(onRejected, resolve, reject)
}
}
}
// 构造函数,传入执行器executor
constructor(executor) {
// 在构造器内部定义resolve和reject函数,防止this指向问题(绑定this)
const resolve = (data) => {
this.#changeState(FULFILLED, data)
}
const reject = (reason) => {
this.#changeState(REJECTED, reason)
}
try {
executor(resolve, reject)
// 只能捕获同步代码的错误,异步代码的错误无法捕获
}
// 如果执行器执行出错,则执行reject
catch (err) {
reject(err)
}
}
// then方法,返回一个新的Promise对象
then(onFulfilled, onRejected){
return new MyPromise((resolve, reject) => {
// 将每次调用then的onFulfilled, onRejected, resolve, reject放到handlers数组中
this.#handlers.push({
onFulfilled,
onRejected,
resolve,
reject
})
// 调用then方法时run方法
this.#run()
})
}
}
// 示例
const p = new MyPromise((resolve, reject) => {
setTimeout(()=>resolve(12), 1000);
})
p.then(result => console.log(result), err => console.log(err))
p.then(result => {
console.log(result)
return new MyPromise((resolve, reject) => {
setTimeout(()=>resolve(24), 1000);
})
}, err => console.log(err))
.then(result => console.log(result))
// 输出:(延迟1秒后)12 12 24 (两个12同时输出,24延迟1秒输出)