十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
本文首发于 vivo互联网技术 微信公众号
链接: https://mp.weixin.qq.com/s/Lp_5BXdpm7G29Z7zT_S-bQ
作者:Morrain成都创新互联专注于企业网络营销推广、网站重做改版、秀英网站定制设计、自适应品牌网站建设、HTML5建站、成都商城网站开发、集团公司官网建设、成都外贸网站建设公司、高端网站制作、响应式网页设计等建站业务,价格优惠性价比高,为秀英等各大城市提供网站开发制作服务。
了用法,原生提供了Promise对象。更多关于 Promise 的介绍请参考阮一峰老师的 ES6入门 之 Promise 对象。
很多同学在学习 Promise 时,知其然却不知其所以然,对其中的用法理解不了。 本系列文章由浅入深逐步实现 Promise,并结合流程图、实例以及动画进行演示,达到深刻理解 Promise 用法的目的。
本系列文章有如下几个章节组成:
图解 Promise 实现原理(一)—— 基础实现
图解 Promise 实现原理(二)—— Promise 链式调用
图解 Promise 实现原理(三)—— Promise 原型方法实现
图解 Promise 实现原理(四)—— Promise 静态方法实现
上一节中,实现了 Promise 的原型方法。包括增加异常状态,catch以及 finally。截至目前,Promise 的实现如下:
class Promise {
callbacks = [];
state = 'pending';//增加状态
value = null;//保存结果
constructor(fn) {
fn(this._resolve.bind(this), this._reject.bind(this));
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
this._handle({
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject
});
});
}
catch(onError) {
return this.then(null, onError);
}
finally(onDone) {
if (typeof onDone !== 'function') return this.then();
let Promise = this.constructor;
return this.then(
value => Promise.resolve(onDone()).then(() => value),
reason => Promise.resolve(onDone()).then(() => { throw reason })
);
}
_handle(callback) {
if (this.state === 'pending') {
this.callbacks.push(callback);
return;
}
let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;
if (!cb) {//如果then中没有传递任何东西
cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
cb(this.value);
return;
}
let ret;
try {
ret = cb(this.value);
cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
} catch (error) {
ret = error;
cb = callback.reject
} finally {
cb(ret);
}
}
_resolve(value) {
if (value && (typeof value === 'object' || typeof value === 'function')) {
var then = value.then;
if (typeof then === 'function') {
then.call(value, this._resolve.bind(this), this._reject.bind(this));
return;
}
}
this.state = 'fulfilled';//改变状态
this.value = value;//保存结果
this.callbacks.forEach(callback => this._handle(callback));
}
_reject(error) {
this.state = 'rejected';
this.value = error;
this.callbacks.forEach(callback => this._handle(callback));
}
}
接下来再介绍一下 Promise 中静态方法的实现,譬如 Promise.resolve、Promise.reject、Promise.all 和 Promise.race。其它静态方法的实现也是类似的。
除了前文中提到的 Promise实例的原型方法外,Promise 还提供了 Promise.resolve 和Promise.reject 方法。用于将非 Promise 实例包装为 Promise 实例。例如:
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
Promise.resolve 的参数不同对应的处理也不同,如果 Promise.resolve 的参数是一个 Promise的实例,那么 Promise.resolve 将不做任何改动,直接返回这个 Promise 实例,如果是一个基本数据类型,譬如上例中的字符串,Promise.resolve 就会新建一个 Promise 实例返回。这样当我们不清楚拿到的对象到底是不是 Promise 实例时,为了保证统一的行为,Promise.resolve 就变得很有用了。看一个例子:
const Id2NameMap = {};
const getNameById = function (id) {
if (Id2NameMap[id]) return Id2NameMap[id];
return new Promise(resolve => {
mockGetNameById(id, function (name) {
Id2NameMap[id] = name;
resolve(name);
})
});
}
getNameById(id).then(name => {
console.log(name);
});
上面的场景我们会经常碰到,为了减少请求,经常会缓存数据,我们获取到 id 对应的名字后,存到 Id2NameMap 对象里,下次再通过 id 去请求 id 对应的 name 时先看 Id2NameMap里有没有,如果有就直接返回对应的 name,如果没有就发起异步请求,获取到后放到 Id2NameMap 中去。
其实上面的代码是有问题的,如果命中 Id2NameMap 里的值,getNameById 返回的结果就是 name,而不是 Promise 实例。此时 getNameById(id).then 会报错。在我们不清楚返回的是否是 Promise 实例的情况下,就可以使用 Promise.resolve 进行包装:
Promise.resolve(getNameById(id)).then(name => {
console.log(name);
});
这样一来,不管 getNameById(id) 返回的是什么,逻辑都没有问题。看下面的Demo:
demo-Promise.resolve 的源码
在实现 Promise.resolve 之前,我们先看下它的参数分为哪些情况:
(1)参数是一个 Promise 实例
如果参数是 Promise 实例,那么 Promise.resolve 将不做任何修改、原封不动地返回这个实例。
(2)参数是一个 thenable 对象
thenable 对象指的是具有 then 方法的对象,比如下面这个对象。
let thenable = {
then: function(onFulfilled) {
onFulfilled(42);
}
};[object Object]
Promise.resolve 方法会将这个对象转为 Promise 对象,然后就立即执行 thenable 对象的 then方法。
let thenable = {
then: function(onFulfilled) {
onFulfilled(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
上面代码中,thenable对象的then方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then方法指定的回调函数,输出 42。
(3)参数不是具有 then 方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then方法的对象,则 Promise.resolve 方法返回一个新的 Promise 对象,状态为 resolved。
(4)不带任务参数
Promise.resolve 方法允许调用时不带参数,直接返回一个 resolved 状态的 Promise 对象。
static resolve(value) {
if (value && value instanceof Promise) {
return value;
} else if (value && typeof value === 'object' && typeof value.then === 'function') {
let then = value.then;
return new Promise(resolve => {
then(resolve);
});
} else if (value) {
return new Promise(resolve => resolve(value));
} else {
return new Promise(resolve => resolve());
}
}