1、什么是Promise
Promise 是 JavaScript 中的一种异步编程解决方案。它可以用来处理异步操作的结果,并在操作完成后返回结果。
Promise 对象有三种状态:pending、fulfilled 和 rejected。
- pending 状态表示操作尚未完成
- fulfilled 状态表示操作已成功完成
- rejected 状态表示操作已失败
Promise 对象可以使用 then 方法来处理 fulfilled 状态和 rejected 状态。
Promise 主要解决的是 JavaScript 中的回调地狱问题。回调地狱是指在 JavaScript 中,由于异步操作的需要,经常需要在回调函数中嵌套调用其他回调函数,形成一个复杂的嵌套结构,导致代码难以维护和阅读。
function callbackHell() {
asyncOperation1(function (result1) {
asyncOperation2(result1, function (result2) {
asyncOperation3(result2, function (result3) {
// do something with result3
});
});
});
}
这段代码中,每个 asyncOperation 方法都需要一个回调函数来处理结果,而且回调函数嵌套在一起,形成了回调地狱。
Promise 可以通过将回调函数封装到 Promise 对象中,并使用 then 方法来处理结果,使得代码更加简洁和易读。
function promiseHell() {
asyncOperation1()
.then(result1 => asyncOperation2(result1))
.then(result2 => asyncOperation3(result2))
.then(result3 => {
// do something with result3
});
}
这段代码中,每个 asyncOperation 方法返回一个 Promise 对象,并使用 then 方法来处理结果。这样就避免了回调地狱的问题,代码更加简洁易读。
2、Promise规范
Promise 是 JavaScript 中异步编程的一种规范,其规范定义了一组 API,可以用来处理异步操作的结果。
Promise 规范定义了以下几个重要的概念:
- Promise 对象:表示一个异步操作的结果。
- resolve 函数:表示异步操作成功完成。
- reject 函数:表示异步操作失败。
- then 方法:用来处理 resolve 函数和 reject 函数的结果。
- catch 方法:用来处理 reject 函数的结果。
- all():将多个 Promise 对象组成的数组传入 all() 方法,当所有 Promise 都 fulfilled 时,调用 then() 注册的回调函数;如果其中有任意一个 Promise rejected,则调用 catch() 注册的回调函数。
- race():将多个 Promise 对象组成的数组传入 race() 方法,当其中任意一个 Promise fulfilled 或 rejected 时,立即调用 then() 或 catch() 注册的回调函数。
- finally():不管 Promise 最终状态是 fulfilled 还是 rejected,都会调用 finally() 注册的回调函数。
Promise 规范是由 ECMAScript 委员会制定的,并在 ECMAScript 2015 (ES6) 中正式被提出。现在几乎所有的浏览器都已经支持了这个规范。
3、Promise基本结构
Promise 的基本结构主要由两部分组成:Promise 对象和 executor 函数。
-
Promise 对象:表示一个异步操作的结果。Promise 对象有三种状态:pending、fulfilled 和 rejected。
-
Executor 函数:用来执行异步操作,并接收 resolve 和 reject 两个函数作为参数。
一般来说,Promise 的基本结构如下:
const promise = new Promise((resolve, reject) => {
// do something asynchronous which eventually calls either:
//
// resolve(someValue); // fulfilled
// or
// reject("failure reason"); // rejected
});
Promise 对象有三种状态,分别为pending、fulfilled 和 rejected。
-
Pending:表示异步操作还未完成
-
Fulfilled:表示异步操作成功完成
-
Rejected:表示异步操作失败
当 executor 函数中调用 resolve 函数时,Promise 状态变为 fulfilled,当调用 reject 函数时,Promise 状态变为 rejected。
通过 then() 和 catch() 方法可以注册回调函数来处理结果,当Promise状态为 fulfilled 时,调用 then()注册的回调函数,当Promise状态为 rejected 时,调用 catch()注册的回调函数。
// Promise是通过构造函数实例化一个对象,然后通过实例对象上的then方法,来处理异步返回的结果
// 有三种状态,分别为pending、fulfilled 和 rejected
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined; //成功结果
// 当Promise对象已经由等待态(Pending)改变为执行态(Fulfilled)或者拒绝态(Rejected)后,就不能再次更改状态,且终值也不可改变
const resolve = (value) => {
// 只能是pending状态的时候才能更改状态
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
}
};
const reject = (reason) => {
// 只能是pending状态的时候才能更改状态
if (this.state === 'pending') {
this.state = 'rejected';
this.value = reason;
}
};
// executor函数本身也会可能存在异常,因此通过try/catch来捕获一下异常情况:
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilled, onRejected) {}
}
4、then实现
then(onFulfilled, onRejected) {
//如果 onFulfilled 或 onRejected 不是函数,则忽略它们
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
if (this.status === 'fulfilled') {
try {
onFulfilled(this.value);
} catch (e) {
reject(e);
}
}
if (this.status === 'rejected') {
try {
onRejected(this.value);
} catch (e) {
reject(e);
}
}
if (this.status === 'pending') {
this.onFulfilledCallbacks.push(() => {
try {
onFulfilled(this.value);
} catch (e) {
reject(e);
}
});
this.onRejectedCallbacks.push(() => {
try {
onRejected(this.reason);
} catch (e) {
reject(e);
}
});
});
}
};
then 方法的实现中首先判断当前 Promise 对象的状态,如果已经 fulfilled,就立即调用 onFulfilled 函数处理结果;如果已经 rejected,就立即调用 onRejected 函数处理错误;如果状态是 pending,就将 onFulfilled 和 onRejected 函数分别加入回调队列,等待状态改变时调用。
5、异步实现
Promise 内部实现异步的原理主要是通过 JavaScript 的事件循环机制实现的。在 JavaScript 中,所有的代码都是在单线程的环境下运行的,也就是说不能同时进行多个任务。为了解决这个问题,JavaScript 提供了事件循环机制,用于管理异步任务。
当执行一个异步任务时,JavaScript 会将该任务放入一个队列中,等待主线程执行。当主线程空闲时,它会从队列中取出一个任务并执行。这样,就可以在不阻塞主线程的情况下执行异步任务。
Promise 内部也是基于这个机制实现的。当我们调用 Promise 的 then 方法时,会将成功回调和失败回调分别放入成功队列和失败队列中,等待主线程执行。当 Promise 的状态变为 resolved 或 rejected 时,主线程会从相应的队列中取出回调并执行。这样就可以保证在执行回调时不会阻塞主线程。
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.onFulfilledCallbacks = []; //成功的回调
this.onRejectedCallbacks = []; //失败的回调
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach((cb) => cb());
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.value = reason;
this.onRejectedCallbacks.forEach((cb) => cb());
}
};
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilled, onRejected) {
// ...省略
if (this.status === 'pending') {
this.onFulfilledCallbacks.push(() => {
try {
onFulfilled(this.value);
} catch (e) {
reject(e);
}
});
this.onRejectedCallbacks.push(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
}
}
6、链式调用实现
可以在 then 方法中返回一个新的 Promise 对象来实现链式调用。
class MyPromise {
// 省略...
then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
try {
const x = onFulfilled(this.value);
resolvePromise(x, resolve, reject);
} catch (e) {
reject(e);
}
}
if (this.state === 'rejected') {
try {
const x = onRejected(this.value);
resolvePromise(x, resolve, reject);
} catch (e) {
reject(e);
}
}
if (this.state === 'pending') {
this.onFulfilledCallbacks.push(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(x, resolve, reject);
} catch (e) {
reject(e);
}
});
this.onRejectedCallbacks.push(() => {
try {
const x = onRejected(this.value);
resolvePromise(x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
return new MyPromise((resolve, reject) => {});
}
}
上面的代码中,在 then 方法中返回了一个新的 MyPromise 对象。
在 onFulfilled 和 onRejected 回调函数中,我们调用 resolvePromise 函数来处理返回值 x。
-
如果 x 是一个普通值,则直接调用 resolve(x)
-
如果 x 是另一个 promise 对象,则继续调用它的 then 方法,直到返回一个普通值为止。
7、resolvePromise实现
resolvePromise 是一个函数,用于处理将一个 promise 作为 then 方法返回值的情况。这是为了遵循 Promise A+ 规范中关于 promise 链式调用的要求。
具体来说,当一个 promise 的 then 方法调用时,如果返回值是一个 promise,那么这个 promise 的状态就会传递给当前 promise。这个过程称为 promise 链式调用。
resolvePromise 函数的实现原理如下:
-
检查 x 是不是 promise。如果是,则使用 x 的状态来更新 promise。
-
如果 x 是一个普通的值,则调用 resolve 函数将 x 作为结果来完成 promise。
-
如果 x 是一个函数或者是一个对象,则调用 x.then 方法,并将 promise 的 resolve 和 reject 作为参数传入。
-
如果 x.then 方法抛出了异常,则调用 promise 的 reject 函数。
举个例子,当我们调用 promise.then(resolvePromise) 时,会在resolvePromise 中接收到 promise的resolve 和 reject 函数,在函数里面根据x的类型进行不同的操作。
这个 resolvePromise 是为了解决promise链式调用时,后面then方法返回的promise状态如何传递到前面promise的问题,从而遵循Promise A+规范。
function resolvePromise(promise, x, resolve, reject) {
// 如果promise和x是同一个对象,抛出类型错误
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
let called = false;
// 如果x是promise对象
if (x instanceof Promise) {
// 如果x的状态是pending,将promise的状态也设置为pending
if (x.status === 'pending') {
x.then(function(value) {
resolvePromise(promise, value);
}, function(reason) {
reject(promise, reason);
});
} else {
// 否则将promise的状态和x的状态保持一致
promise.status = x.status;
promise.value = x.value;
promise.resolveCallbacks.forEach(cb => cb(promise.value));
}
} else if (x && (typeof x === 'object' || typeof x === 'function')) {
// 如果x是一个thenable
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, function (y) {
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
}, function (r) {
if (called) return;
called = true;
reject(r);
});
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
8、完整代码
class MyPromise {
constructor(executor) {
this.state = 'pending'; // promise状态
this.value = undefined; // 返回值
this.onFulfilledCallbacks = []; //成功的回调
this.onRejectedCallbacks = []; // 失败的回调
const resolve = (value) => {
if(this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach((cb) => cb());
}
};
const reject = (reason) => {
if(this.state === 'pending') {
this.state = 'rejected';
this.value = reason;
this.onRejectedCallbacks.forEach((cb) => cb());
}
};
try {
executor(resolve, reject);
} catch(e) {
reject(e);
}
}
then(onFulfilled, onRejected) {
let promise2 = new MyPromise((resolve, reject) => {
if(this.state === 'fulfilled') {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
}
if(this.state === 'rejected') {
setTimeout(() => {
try {
const x = onRejected(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
}
if(this.state === 'pending') {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
});
}
});
return promise2
}
}
function resolvePromise(promise, x, resolve, reject) {
// 如果promise和x是同一个对象,抛出类型错误
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
let called = false;
// 如果x是promise对象
if (x instanceof Promise) {
// 如果x的状态是pending,将promise的状态也设置为pending
if (x.status === 'pending') {
x.then(function(value) {
resolvePromise(promise, value);
}, function(reason) {
reject(promise, reason);
});
} else {
// 否则将promise的状态和x的状态保持一致
promise.status = x.status;
promise.value = x.value;
promise.resolveCallbacks.forEach(cb => cb(promise.value));
}
} else if (x && (typeof x === 'object' || typeof x === 'function')) {
// 如果x是一个thenable
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, function (y) {
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
}, function (r) {
if (called) return;
called = true;
reject(r);
});
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}