promise底层实现
Posted zhouyideboke
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了promise底层实现相关的知识,希望对你有一定的参考价值。
1.promise常用方法
Promise.all() Promise.race() Promise.resolve() Promise.reject() Promise.prototype.catch() Promise.prototype.finally() Promise.prototype.then()
2.考虑下面一种获取用户id的请求处理
//例1
function getUserId() {
return new Promise(function(resolve) {
//异步请求
http.get(url, function(results) {
resolve(results.id)
})
})
}
getUserId().then(function(id) {
//一些处理
})
getUserId
方法返回一个promise
,可以通过它的then
方法注册(注意注册
这个词)在promise
异步操作成功时执行的回调。这种执行方式,使得异步调用变得十分顺手。
原理剖析
那么类似这种功能的Promise
怎么实现呢?其实按照上面一句话,实现一个最基础的雏形还是很easy的。
this.then = function (onFulfilled) {
callbacks.push(onFulfilled);
return this;
};
getUserId().then(function (id) {
// 一些处理
}).then(function (id) {
// 一些处理
});
function Promise(fn) {
var state = ‘pending‘,
value = null,
callbacks = [];
this.then = function (onFulfilled) {
if (state === ‘pending‘) {
callbacks.push(onFulfilled);
return this;
}
onFulfilled(value);
return this;
};
function resolve(newValue) {
value = newValue;
state = ‘fulfilled‘;
setTimeout(function () {
callbacks.forEach(function (callback) {
callback(value);
});
}, 0);
}
fn(resolve);
}
function Promise(fn) {
var state = ‘pending‘,
value = null,
callbacks = [];
this.then = function (onFulfilled) {
return new Promise(function (resolve) {
handle({
onFulfilled: onFulfilled || null,
resolve: resolve
});
});
};
function handle(callback) {
if (state === ‘pending‘) {
callbacks.push(callback);
return;
}
//如果then中没有传递任何东西
if(!callback.onFulfilled) {
callback.resolve(value);
return;
}
var ret = callback.onFulfilled(value);
callback.resolve(ret);
}
function resolve(newValue) {
if (newValue && (typeof newValue === ‘object‘ || typeof newValue === ‘function‘)) {
var then = newValue.then;
if (typeof then === ‘function‘) {
then.call(newValue, resolve);
return;
}
}
state = ‘fulfilled‘;
value = newValue;
setTimeout(function () {
callbacks.forEach(function (callback) {
handle(callback);
});
}, 0);
}
fn(resolve);
}
getUserId()
.then(getUserJobById)
.then(function (job) {
// 对job的处理
});
function getUserJobById(id) {
return new Promise(function (resolve) {
http.get(baseUrl + id, function(job) {
resolve(job);
});
});
}
then
方法中,创建并返回了新的Promise
实例,这是串行Promise
的基础,并且支持链式调用。handle
方法是promise
内部的方法。then
方法传入的形参onFulfilled
以及创建新Promise
实例时传入的resolve
均被push
到当前promise
的callbacks
队列中,这是衔接当前promise
和后邻promise
的关键所在(这里一定要好好的分析下handle的作用)。getUserId
生成的promise
(简称getUserId promise
)异步操作成功,执行其内部方法resolve
,传入的参数正是异步操作的结果id
- 调用
handle
方法处理callbacks
队列中的回调:getUserJobById
方法,生成新的promise
(getUserJobById promise
) - 执行之前由
getUserId promise
的then
方法生成的新promise
(称为bridge promise
)的resolve
方法,传入参数为getUserJobById promise
。这种情况下,会将该resolve
方法传入getUserJobById promise
的then
方法中,并直接返回。 - 在
getUserJobById promise
异步操作成功时,执行其callbacks
中的回调:getUserId bridge promise
中的resolve
方法 - 最后执行
getUserId bridge promise
的后邻promise
的callbacks
中的回调。
//例5
function getUserId() {
return new Promise(function(resolve) {
//异步请求
http.get(url, function(error, results) {
if (error) {
reject(error);
}
resolve(results.id)
})
})
}
getUserId().then(function(id) {
//一些处理
}, function(error) {
console.log(error)
})
有了之前处理fulfilled
状态的经验,支持错误处理变得很容易,只需要在注册回调、处理状态变更上都要加入新的逻辑:
function Promise(fn) {
var state = ‘pending‘,
value = null,
callbacks = [];
this.then = function (onFulfilled, onRejected) {
return new Promise(function (resolve, reject) {
handle({
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject
});
});
};
function handle(callback) {
if (state === ‘pending‘) {
callbacks.push(callback);
return;
}
var cb = state === ‘fulfilled‘ ? callback.onFulfilled : callback.onRejected,
ret;
if (cb === null) {
cb = state === ‘fulfilled‘ ? callback.resolve : callback.reject;
cb(value);
return;
}
ret = cb(value);
callback.resolve(ret);
}
function resolve(newValue) {
if (newValue && (typeof newValue === ‘object‘ || typeof newValue === ‘function‘)) {
var then = newValue.then;
if (typeof then === ‘function‘) {
then.call(newValue, resolve, reject);
return;
}
}
state = ‘fulfilled‘;
value = newValue;
execute();
}
function reject(reason) {
state = ‘rejected‘;
value = reason;
execute();
}
function execute() {
setTimeout(function () {
callbacks.forEach(function (callback) {
handle(callback);
});
}, 0);
}
fn(resolve, reject);}
上述代码增加了新的reject
方法,供异步操作失败时调用,同时抽出了resolve
和reject
共用的部分,形成execute
方法。
错误冒泡是上述代码已经支持,且非常实用的一个特性。在handle
中发现没有指定异步操作失败的回调时,会直接将bridge promise
(then
函数返回的promise
,后同)设为rejected
状态,如此达成执行后续失败回调的效果。这有利于简化串行Promise
的失败处理成本,因为一组异步操作往往会对应一个实际功能,失败处理方法通常是一致的:
//例6
getUserId()
.then(getUserJobById)
.then(function (job) {
// 处理job
}, function (error) {
// getUserId或者getUerJobById时出现的错误
console.log(error);
});
function handle(callback) {
if (state === ‘pending‘) {
callbacks.push(callback);
return;
}
var cb = state === ‘fulfilled‘ ? callback.onFulfilled : callback.onRejected,
ret;
if (cb === null) {
cb = state === ‘fulfilled‘ ? callback.resolve : callback.reject;
cb(value);
return;
}
try {
ret = cb(value);
callback.resolve(ret);
} catch (e) {
callback.reject(e);
}
}
如果在异步操作中,多次执行resolve
或者reject
会重复处理后续回调,可以通过内置一个标志位解决。
总结
- 通过Promise.prototype.then和Promise.prototype.catch方法将观察者方法注册到被观察者Promise对象中,同时返回一个新的Promise对象,以便可以链式调用。
- 被观察者管理内部pending、fulfilled和rejected的状态转变,同时通过构造函数中传递的resolve和reject方法以主动触发状态转变和通知观察者
以上是关于promise底层实现的主要内容,如果未能解决你的问题,请参考以下文章