学习呦
14 10月 2025 21 min read

Promise

Written by 米豆

Promise

概念

Promise可以用于顺序的执行一些异步的代码。比如说,需要先获取用户的信息,得到了用户的基本信息才可以去用基本信息中的userId拿到用户的权限信息。 Promise可以处理一个不知道返回结果的方法。比如,获取学校的信息需要调用接口,但是我们没法保证这个接口每次调用100%正确(有可能会因为网络问题等出现异常)

状态state、结果result

这俩均为Promise对象的内部属性,可以访问 一个Promise执行过程中,这两个内部属性的变换

https://static.xuyanshe.club/img/1675753033811-6522948b-9f63-4bc6-bc6c-248ec5f1c1c9.png

resolve(): pending -> fulfilled

reject():pending -> rejected

如果一个Promise执行完了resolve或者reject,那么这个Promise的状态是settled

https://static.xuyanshe.club/img/1675752301178-c3549495-57c0-406a-bc43-e8f131d26d33.jpeg

使用

new Promise(function(resolve, reject) {  
// executor
// 这里如果用return返回,返回的东西没有任何作用
});

接收一个resolve函数和reject函数。Promise接收的函数的函数体会自动的执行,执行的结果如果成功就会执行resolve失败就会执行reject,也可以自己调用resolve和reject

resolve、reject执行结果的接收

使用then()来接收

promise.then(
function(result) { /* handle a successful result */ },
function(error) { /* handle an error */ }
);

then也可以接收两个参数,第一个是处理resolve执行的结果(该函数将在 promise resolved 且接收到结果后执行),第二个是reject执行的结果(该函数将在 promise rejected 且接收到 error 信息后执行) 当然也可以只写一个函数作为参数

.then((result) => {})
.then(null, errorHandlingFunction)

上面代码块仅处理error也可以使用catch

let promise = new Promise((resolve, reject) => {  
setTimeout(() => reject(new Error("Whoops!"))
, 1000);
});
// .catch(f) 与 promise.then(null, f) 一样promise.catch(alert);

catch可以捕获Promise以及resolve产生的错误。且在链式调用下可以捕获前面的的所有错误

最后可以用finally来执行收尾工作

new Promise((resolve, reject) => {  
/* 做一些需要时间的事,之后调用可能会 resolve 也可能会 reject */
})
// 在 promise 为 settled 时运行,无论成功与否
.finally(() => stop loading indicator)

finally会接收上面执行成功的value或者执行失败的error

Promise链

一个Promise的resolve和reject可以返回,返回的值为一个新的Promise

loadScript("/article/promise-chaining/one.js")  
.then(script => loadScript("/article/promise-chaining/two.js"))
.then(script => loadScript("/article/promise-chaining/three.js"))
.then(script => { // 脚本加载完成,我们可以在这儿使用脚本中声明的函数
one();
two();
three();
});

如果.then()这样调用没有传入onFulfilled就会透传,直到遇到有回调的then方法
.catch()就和then不同,如果前面的Promise Reject了,就会去找有没有调用catch方法,会跳过之间的所有then()(注意是没有添加 onRejected 回调的.then())

自己实现一个MyPromise

Promise是异步的,关键在于如何实现”异步”的功能。不阻塞主线程的执行,这里自己实现使用queueMicrotask来执行resolve和reject
thenable方法也是难点,要考虑传入的两个方法的返回值的情况,Promise A+也就这些东西

function MyPromise(callback) {
this.state = MyPromiseState.Pending;
this.onFulfilleds = [];
this.onRejecteds = [];
this.value = null

this.resolve = data => {
if (data instanceof MyPromise) {
return data.then(this.resolve, this.reject)
}
queueMicrotask(() => {
if (this.state === MyPromiseState.Pending) {
this.state = MyPromiseState.Fulfilled;
this.value = data
for (let i = 0; i < this.onFulfilleds.length; i++) {
this.onFulfilleds[i](data);
}
}
})
}

this.reject = err => {
queueMicrotask(() => {
if (this.state === MyPromiseState.Pending) {
this.state = MyPromiseState.Rejected;
this.value = err;
for (let i = 0; i < this.onRejecteds.length; i++) {
this.onRejecteds[i](err);
}
}
})
}

try {
callback(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}


MyPromise.prototype.then = function (onFulfilled, onRejected) {

// 参数归一化
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }

// self 相当于上一个Promise
// 因为Promise的链式调用
// 第二个Promise的状态会和第一个Promise的状态相关联
let self = this
let newPromise = new MyPromise((resolve, reject) => {
if (self.state === MyPromiseState.Pending) {
// 如果是Pending状态,暂时不执行回调
// 可以考虑将回调存储起来,等到状态改变时再执行
self.onFulfilleds.push(() => {
try {
let x = onFulfilled(self.value)
solutionX(newPromise, x, resolve, reject)
} catch (error) {
reject(error)
}
})
self.onRejecteds.push(() => {
try {
let x = onRejected(self.value)
solutionX(newPromise, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}

if (self.state === MyPromiseState.Fulfilled) {
queueMicrotask(() => {
try {
let x = onFulfilled(self.value)
solutionX(newPromise, x, resolve, reject)
} catch (error) {
reject(error)
}
});
}

if (self.state === MyPromiseState.Rejected) {
queueMicrotask(() => {
try {
let x = onRejected(self.value)
solutionX(newPromise, x, resolve, reject)
} catch (error) {
reject(error)
}
});
}
});
return newPromise;
}

// 这个方法是处理onFulfilled、onRejected回调的返回值的
// 返回值根据Promise A+规范
// 要考虑是否返回了当前.then调用前的Promise
// 是否返回了一个新的Promise
// 是否返回了一个对象或方法,里面是不是正好有一个叫then的方法
// 除此之外的其他类型的值
const solutionX = (promise, x, promiseResolve, promiseReject) => {
if (Object.is(promise, x)) {
return promiseReject(new TypeError('x is not same to be promise'))
}
if (x instanceof MyPromise) {
if (x.state === MyPromiseState.Pending) {
x.then(
value => solutionX(promise, value, promiseResolve, promiseReject),
promiseReject
)
}
if (x.state === MyPromiseState.Fulfilled || x.state === MyPromiseState.Rejected) {
x.then(
promiseResolve, promiseReject
)
}
return
}
let called = false;
if (!x) {
promiseResolve(x)
} else if (typeof x === 'function' || typeof x === 'object') {
try {
let then = x.then
if (typeof then === 'function') {
then.apply(
x,
[
y => {
if (called) return
called = true
solutionX(promise, y, promiseResolve, promiseReject)
},
r => {
if (called) return
called = true
promiseReject(r)
}
]
)
} else {
promiseResolve(x)
}
} catch (error) {
if (called) return
called = true

promiseReject(error)
}
} else {
promiseResolve(x)
}
}

Promise A+规范测试

pnpx promises-aplus-tests myPromise.js

结果通过所有872个测试用例

872 passing (20s)

回调地狱问题

比如上面代码块中需要按顺序以此请求对应的js文件,如果不使用Promise

loadScript('1.js', function(error, script) {  
if (error) {
handleError(error);
} else {
// ...
loadScript('2.js', function(error, script) {
if (error) {
handleError(error);
} else {
// ...
loadScript('3.js', function(error, script) {
if (error) {
handleError(error);
} else {
// ...加载完所有脚本后继续 (*)
}
});
}
});
}});

没有发生错误的情况下,又会执行loadScript,之后在其中又判断是否有错误,没错误继续load…,上面的代码就是“回调地狱”

Promise定义的一些静态方法

Promise.all

概念:并行执行多个Promise,并且等待所有的Promise执行完毕

Promise.all([  
new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1
new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3
])
.then(alert);
// 1,2,3 当上面这些 promise 准备好时:每个 promise 都贡献了数组中的一个元素

all()接收一个数组,里面是Promise对象,返回一个res数组,并且会按照Promise数组的顺序组装res结果数组 如果执行的任意一个Promise被reject,则整个Promise.all()会立即的reject并且返回error

Promise.race

和上面的.allAPI都可以同时执行多个Promise,但是.race仅等待第一个状态为settled的Promise(settled就是Promise要么resolve要么reject)

Promise.any

和.any也是可以同时执行多个,但.any等待第一个fulfilled状态的Promise,并返回这个Promise(fulfilled就是Promise对象resolve了,而不是reject)

Promise.allSettled

和.all()都是接收一个Promise对象数组,但是不会像all()碰到reject状态的Promise就返回,而是等待数组的所有Promise执行完毕(如果是resolve,返回的{status: 'fulfilled', value: ''}对象,如果是reject,返回的{status: 'rejected', reason: ''}对象)

↑