Promise是JavaScript ES6规范中的一个重要对象,可以方便地实现各种异步操作。
以下是各种浏览器对Promise的支持情况:
对于不支持Promise的浏览器我们可以自定义Promise实现,将其作为原生Promise的Polyfill。
以下是自定义实现的Promise:
/**
* 自定义实现Promise
* https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
*
* 核心:
* promise的resolve()或reject()执行,表示status状态变化,并将结果作为入参触发watcher的onFullfill()或onReject()方法
*
* 逻辑:
* 1. promise的状态是外部通过调用executor中的resolve()和reject()方法实现的
* 2. 当调用resolve()的时候,promise的状态从pending变成fullfilled,并执行通过.then()监听fullfilled状态的回调,且resovle()传入的结果会作为onFullfill()回调的入参
* 3. 当调用reject()的时候,promise的状态从pending变成rejected,并执行通过.then()监听rejected状态的回调,且reject()传入的错误会作为onReject()回调的入参
*
* case分析:
* p1.then(p1OnFullfilled, p1OnReject) => p2
* 1. p1OnFullfilled执行
* 1.1 该方法的返回值是Promise,该Promise是一个新的Promise p3,不是p2,当这个新的Promise执行resovle()时,其resolve()传入的结果会作为p2的onFullfill()的回调入参
* 1.2 该方法的返回值不是Promise,该值作为入参触发p2._resolve()执行
* 2. p1OnReject执行
* 2.1 该方法内部通过throw抛出异常,该异常作为入参触发执行p2._reject()执行
* 2.2 该方法内部没有抛出异常,则执行p2的onFullfill()回调,且返回值会作为p2的onFullfill()的回调参数
*
* @param executor function,Required,其签名格式为function(resolve, reject)。
* executor是带有 resolve 和 reject 两个参数的函数。
* Promise构造函数执行时立即调用executor 函数,resolve 和 reject 两个函数作为参数传递给executor(executor 函数在Promise构造函数返回所建promise实例对象前被调用)。
* resolve 和 reject 函数被调用时,分别将promise的状态改为fulfilled(完成)或rejected(失败)。
* executor 内部通常会执行一些异步操作,一旦异步操作执行完毕(可能成功/失败),要么调用resolve函数来将promise状态改成fulfilled,要么调用reject 函数将promise的状态改为rejected。
* 如果在executor函数中抛出一个错误,那么该promise 状态为rejected。executor函数的返回值被忽略。
*/
function MyPromise(executor) {
this._status = 0; // 0表示pending,1表示fullfilled,-1表示rejected
this._result = undefined; // resolve()执行的入参
this._err = null; // reject()执行的入参
// watcher是通过then来观察结果的Promise,由于可以通过多次调用p1.then()来得到多个新的Promise,所以此处是watcher数组
this._watchers = [];
/**
* 封装传递给executor()的resolve()方法
* 逻辑:
* 1. 更改当前promise的status,并记录result
* 2. 通知watchers结果
*
* @parma result Any,Optional,Promise的resovle的结果值
*/
this._resolve = (result) => {
// Promise已经resolved或rejected,不能再次resolve
if (this._status !== 0) {
return;
}
this._status = 1;
this._result = result;
for (const watcher of this._watchers) {
this._notifyWatcher(watcher);
}
};
/**
* 封装传递给executor()的reject()方法
* 逻辑:
* 1. 更改当前promise的status,并记录error
* 2. 通知watchers错误
*
* @param err Any,Optional,Promise的reject的原因
*/
this._reject = (err) => {
// Promise已经resolved或rejected,不能再次reject
if (this._status !== 0) {
return;
}
this._status = -1;
this._err = err;
for (const watcher of this._watchers) {
this._notifyWatcher(watcher);
}
};
try {
executor.call(this, this._resolve, this._reject);
} catch (e) {
this._reject(e);
}
}
/**
* 内部私有方法,用于向当前promise中添加一个watcher以监听当点promise的resolve()或reject()的结果
*
* @param watcher MyPromise,Required,监听当前promise的resolve()或reject()结果的观察者Promise对象
*/
MyPromise.prototype._addWatcher = function(watcher) {
if (this._watchers.indexOf(watcher) < 0) {
this._watchers.push(watcher);
}
if (this._status !== 0) {
// 已经resolved或rejected
this._notifyWatcher(watcher);
}
};
/**
* 内部私有方法,用于将当前promise的resolve()或reject()的结果通知观察者
*
* @param watcher MyPromise,Required,监听当前promise的resolve()或reject()结果的观察者Promise对象
*/
MyPromise.prototype._notifyWatcher = function(watcher) {
if (this._watchers.includes(watcher)) {
if (this._status > 0) {
// 已经fullfilled
setTimeout(() => {
this._removeWatcher(watcher);
watcher._onPrentResolve(this._result);
}, 0);
} else if (this._status < 0) {
// 已经rejected
setTimeout(() => {
this._removeWatcher(watcher);
watcher._onParentReject(this._err);
}, 0);
}
}
};
/**
* 内部私有方法,用于将监听当前promise的resolve()或reject()的观察者从观察者列表中删除
*
* @param watcher MyPromise,Required,监听当前promise的resolve()或reject()结果的观察者Promise对象
*/
MyPromise.prototype._removeWatcher = function(watcher) {
const index = this._watchers.indexOf(watcher);
if (index >= 0) {
this._watchers.splice(index, 1);
}
};
/**
* 功能:
* then() 方法返回一个 Promise。它最多需要有两个参数:Promise 的成功和失败情况的回调函数。
* https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
*
* 逻辑:
* promise1.then()方法需要返回一个新的Promise对象promise2
*
* @param onPromise1Resolve function,Optional,当 Promise 变成接受状态(fulfilled)时调用的函数。
* 该函数有一个参数,即接受的最终结果(the fulfillment value)。
* 如果该参数不是函数,则会在内部被替换为 (x) => x,即原样返回 promise 最终结果的函数。
*
* @param onPromise1Reject function,Optional,当 Promise 变成拒绝状态(rejected)时调用的函数。
* 该函数有一个参数,即拒绝的原因(rejection reason)。
* 如果该参数不是函数,则会在内部被替换为一个 "Thrower" 函数 (it throws an error it received as argument)。
*
* @return MyPromise,返回新的Promise对象,具体的返回值依据以下规则返回。如果 then 中的回调函数:
* 返回了一个值,那么 then 返回的 Promise 将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。
* 没有返回任何值,那么 then 返回的 Promise 将会成为接受状态,并且该接受状态的回调函数的参数值为 undefined。
* 抛出一个错误,那么 then 返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。
* 返回一个已经是接受状态的 Promise,那么 then 返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。
* 返回一个已经是拒绝状态的 Promise,那么 then 返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。
* 返回一个未定状态(pending)的 Promise,那么 then 返回 Promise 的状态也是未定的,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。
*/
MyPromise.prototype.then = function(onPromise1Resolve, onPromise1Reject) {
const promise1 = this;
const promise2 = new MyPromise(function(resolve, reject) {
// 此处的this执行新的Promise p
this._onPrentResolve = function(promise1Result) {
let value = undefined;
// 如果未设置onPromise1Resolve,则直接将promise2进行resolve(promise1Result)
if (!onPromise1Resolve) {
resolve(promise1Result);
return;
}
try {
// 执行promise1的resovle之后的回调方法onPromise1Resolve()
value = onPromise1Resolve(promise1Result);
} catch (e) {
// 如果onPromise1Resolve()方法执行异常,则将对promise2执行reject()
reject(e);
return;
}
if (value instanceof MyPromise) {
// 返回了一个新的Promise对象promise3
const promise3 = value;
// promise2需要等待新的Promise执行完成才能resolve或reject
// 首先覆写promise2._onPrentResolve和promise2._onParentReject方法以便接受promise3执行resolve、reject时的数据
this._onPrentResolve = function(promise3Result) {
resolve(promise3Result);
};
this._onParentReject = function(promise3Err) {
reject(promise3Err);
};
// 使得promise2观察promise3的状态
promise3._addWatcher(this); // this是promise2
} else {
resolve(value);
}
};
this._onParentReject = function(promise1Err) {
let value = undefined;
// 如果未设置onPromise1Reject,则直接将promise2进行reject(promise1Err)
if (!onPromise1Reject) {
reject(promise1Err);
return;
}
try {
// 执行promise1的reject之后的回调方法onPromise1Reject()
value = onPromise1Reject(promise1Err);
} catch(e) {
// 如果onPromise1Reject()方法执行异常,则将对promise2执行reject()
reject(e);
return;
}
if (value instanceof MyPromise) {
// 返回了一个新的Promise对象promise3
const promise3 = value;
// promise2需要等待新的Promise执行完成才能resolve或reject
// 首先覆写promise2._onPrentResolve和promise2._onParentReject方法以便接受promise3执行resolve、reject时的数据
this._onPrentResolve = function(promise3Result) {
resolve(promise3Result);
};
this._onParentReject = function(promise3Err) {
reject(promise3Err);
};
// 使得promise2观察promise3的状态
promise3._addWatcher(this); // this是promise2
} else {
resolve(value);
}
};
// 使得promise2观察promise1的状态
promise1._addWatcher(this); // this是promise2
});
return promise2;
};
/**
* 功能:
* catch() 方法返回一个Promise,并且处理拒绝的情况。它的行为与调用Promise.prototype.then(undefined, onRejected) 相同。
* (事实上, obj.catch(onRejected) 内部实现就是调用 obj.then(undefined, onRejected))
* https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch
*
* 逻辑:
* 1. 对于p1.then(onResolve, onReject),onResolve中出现异常时,onReject不会捕获
* 2. 对于p1.then(onResolve).catch(onCatch),onResolve中出现异常时,onCatch会捕获
* 3. 对于p1.then(onResolve, onReject).catch(onCatch),onResolve中出现异常时,onReject不会捕获,onCatch会捕获
* 4. 对于p1.then(onResolve, onReject).catch(onCatch),onReject中出现异常时,onCatch会捕获
*
* @return MyPromise,返回新的Promise对象
*/
MyPromise.prototype.catch = function(onCatch) {
return this.then(null, onCatch);
};
/**
* 功能:
* finally() 方法返回一个Promise。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。
* 这为在Promise是否成功完成后都需要执行的代码提供了一种方式。这避免了同样的语句需要在then()和catch()中各写一次的情况。
* https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally
*
* 逻辑:
* finally() 虽然与 .then(onFinally, onFinally) 类似,它们不同的是:
* 调用内联函数时,不需要多次声明该函数或为该函数创建一个变量保存它。
* 由于无法知道promise的最终状态,所以finally的回调函数中不接收任何参数,它仅用于无论最终结果如何都要执行的情况。
* 与Promise.resolve(2).then(() => {}, () => {}) (resolved的结果为undefined)不同,Promise.resolve(2).finally(() => {}) resolved的结果为 2。
* 同样,Promise.reject(3).then(() => {}, () => {}) (resolved 的结果为undefined), Promise.reject(3).finally(() => {}) rejected 的结果为 3。
*
* @return MyPromise,返回新的Promise对象
*/
MyPromise.prototype.finally = function(onFinally) {
const onParentResolve = function(result) {
// 忽略onFinally()的执行结果
onFinally();
// 将之前promise的结果透传
return result;
};
const onParentReject = function(err) {
// 忽略onFinally()的执行结果
onFinally();
// 将之前promise的错误透传
throw err;
};
return this.then(onParentResolve, onParentReject);
};
/**
* 功能:
* Promise.resolve(value)方法返回一个以给定值解析后的Promise 对象。
* 如果这个值是一个 promise,那么将返回这个promise;
* 如果这个值是thenable(即带有"then" 方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;
* 否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。
* https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve
*
* @param value Any,Optional,将被Promise对象解析的参数,也可以是一个Promise对象,或者是一个thenable。
*
* @return MyPromise,返回一个带着给定值解析过的Promise对象,如果参数本身就是一个Promise对象,则直接返回这个Promise对象。
*/
MyPromise.resolve = function(value) {
if (value instanceof MyPromise) {
return value;
} else {
return new MyPromise(function(resolve) {
resolve(value);
});
}
};
/**
* Promise.reject()方法返回一个带有拒绝原因的Promise对象。
* https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject
*
* @param reason Any,Optional,表示Promise被拒绝的原因。
*
* @retrun MyPromise,返回一个给定原因了的被拒绝的Promise。
*/
MyPromise.reject = function(reason) {
return new MyPromise(function(resolve, reject) {
reject(reason);
});
};
/**
* Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);
* 如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。
* https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
*
* @param iterable Iterable,Required,一个可迭代对象,如Array。
*
* @return MyPromise,返回一个Promise对象。
* 如果传入的参数是一个空的可迭代对象,则返回一个已完成(already resolved)状态的 Promise。
* 如果传入的参数不包含任何 promise,则返回一个异步完成(asynchronously resolved) Promise。注意:Google Chrome 58 在这种情况下返回一个已完成(already resolved)状态的 Promise。
* 其它情况下返回一个处理中(pending)的Promise。这个返回的 promise 之后会在所有的 promise 都完成或有一个 promise 失败时异步地变为完成或失败。 返回值将会按照参数内的 promise 顺序排列,而不是由调用 promise 的完成顺序决定。
*/
MyPromise.all = function(iterable) {
return new MyPromise(function(resolve, reject) {
const that = this;
if (iterable.length === 0) {
// 如果传入的参数是一个空的可迭代对象,则返回一个已完成(already resolved)状态的 Promise。
resolve([]);
} else {
const promises = iterable.map(item => {
if (item instanceof MyPromise) {
return item;
} else {
// 将非MyPromise实例的值封装成resolved的Promise
return MyPromise.resolve(item);
}
});
const allCount = promises.length;
const resultsObj = {}; // {index: value}
function handleResolve(index, result) {
// 如果promise已经resolved或rejected,则不再继续后续逻辑
if (that._status !== 0) {
return;
}
resultsObj[index] = result;
const keys = Object.keys(resultsObj);
if (keys.length === allCount) {
// 所有promises都已经处理完成
const results = [];
keys.forEach(key => {
const value = resultsObj[key];
const index = parseInt(key);
results[index] = value;
});
resolve(results);
}
}
function handleReject(err) {
// 如果promise已经resolved或rejected,则不再继续后续逻辑
if (that._status !== 0) {
return;
}
reject(err);
}
promises.forEach((promise, index) => {
promise.then(function(result) {
handleResolve(index, result);
}, function(err) {
handleReject(err);
});
});
}
});
};
/**
* Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
* https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/race
*
* 逻辑:
* race 函数返回一个 Promise,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个。
* 如果迭代iterable是空的,则返回的 promise 将永远等待。
* 如果迭代iterable中包含非Promise值、已经resolved的Promise对象、已经rejected的Promise对象, 则 Promise.race() 将解析为迭代iterable中找到的第一个值。
*
* @param iterable Iterable,Required,一个可迭代对象,如Array。
*
* @return MyPromise,返回一个Promise对象。只要给定的迭代iterable中的一个promise解决或拒绝,就采用第一个promise的值作为它的值,从而异步地解析或拒绝(一旦堆栈为空)。
*
*/
MyPromise.race = function(iterable) {
return new MyPromise(function(resolve, reject) {
// 如果迭代iterable是空的,则返回的 promise 将永远等待。
if (iterable.length === 0) {
return;
}
iterable.map(item => {
if (item instanceof MyPromise) {
item.then((result) => {
if (this._status === 0) {
resolve(result);
}
}, (err) => {
if (this._status === 0) {
reject(err)
}
});
} else {
if (this._status === 0) {
resolve(item);
}
}
});
});
};
通过以下方式将其作为Promise Polyfill使用:
<script type="text/javascript" src="promise-polyfill.js"></script>
<script type="text/javascript">
if (!window.Promise) {
window.Promise = MyPromise;
}
</script>
MDN - 使用 Promise
MDN - Promise