一、Promise 原理:
Promise 原理围绕以下三个问题进行解决:
(有任何一步不了解的话请先往下看手写Promise,会一步步剖析原理,看完后再继续回顾这里!!)
1. 怎么实现异步?
Promise内部then函数注册后续需要执行的函数,resolve函数执行。需要保证函数在执行前都已注册好,
解決:resolve内部执行函数的代码需要加入延时机制setTimeout(0)放在任务队列的末尾
思考新问题:如果Promise异步操作已经成功,这时,在异步操作成功之前注册的回调都会执行,但是在Promise异步操作成功这之后调用的then注册的回调就再也不会执行了 (加入状态机制即可解决)
2. 怎么实现在Promise异步操作成功这之后调用的then注册的回调也会执行?
加入状态机制,若为pending,则将函数注册,等待后续resolve调用。若为fulfilled,则立即执行resolve函数,并将状态设为fulfilled
3. 怎么实现链式调用?
若是检测到resolve函数的参数newValue是一个promise对象(有then函数的对象),就使用对象冒充,
调用它的then函数(下一个函数newValue,和它的resolve)
二、手写Promise
1. 极简promise雏形
function Promise(fn) {
var value = null,
callbacks = []; //callbacks为数组,因为可能同时有很多个回调
this.then = function (onFulfilled) {
callbacks.push(onFulfilled);
};
function resolve(value) {
callbacks.forEach(function (callback) {
callback(value);
});
}
fn(resolve);
}
- 调用then方法,将想要在Promise异步操作成功时执行的回调放入callbacks队列,其实也就是注册回调函数,可以向观察者模式方向思考;
- 创建Promise实例时传入的函数会被赋予一个函数类型的参数,即resolve,它接收一个参数value,代表异步操作返回的结果,当异步操作执行成功后,用户会调用resolve方法,这时候其实真正执行的操作是将callbacks队列中的回调一一执行;
2. 加入延时机制
原因:需要保证函数在执行前都已注册好
实现:通过setTimeout机制,将resolve中执行回调的逻辑放置到JS任务队列末尾,以保证在resolve执行时,then方法的回调函数已经注册完成。(若不清楚为什么能放在js任务队列末尾,请自行了解js事件循环机制)
改造resolve函数
function resolve(value) {
setTimeout(function() {
callbacks.forEach(function (callback) {
callback(value);
});
}, 0)
}
3. 加入状态机制
它总共有三种状态:pending(默认,等待状) fulfilled(成功状) rejected(失败状)
状态一旦改变,就不会改变
- 在then函数中,若状态为pending,则将函数注册,等待后续resolve调用。
- 若状态为fulfilled,则立即执行resolve函数,将状态设为fulfilled
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);
}
4. 实现链式Promise
所谓的链式Promise:
链式Promise是指在当前promise达到fulfilled状态后,即开始进行下一个promise(后邻promise)。那么我们如何衔接当前promise和后邻promise呢?(这是这里的难点)。
实现:在then方法里面return一个promise就好啦。传递then方法生成的新promise的resolve方法,resolve(后邻promise),在resolve中,检测到参数是一个promise对象(有then函数的对象),就使用对象冒充,将改变后邻promise的resolve指向当前resolve(即将resolve传到后邻promise的then,并直接返回)
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 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;
}
// 使用try-catch捕获错误
try {
ret = cb(value);
callback.resolve(ret);
} catch (e) {
callback.reject(e);
}
}
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);
}
后话
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,欢迎关注小编,一起涨知识。