手写Promise A+规范的Promise¶
then方法¶
- 一个promise必须提供一个then方法,用来获取当前异步操作的value或error
- 一个promise的then方法接受两个参数:promise.then(onFulfilled,onRejected)
- then方法返回的也是一个新的promise实例。因此可以采用链式写法,即then方法后再调用另一个then方法
手写Promise¶
首先需要做什么?¶
第一步需要创建一个container(容器,可通过其构建一个容器实例),容器的大致作用如下:
- 容器实例化的对象con中首先需要一个用于存储回调函数的数组
- con中需要有一些必要的方法,如add(向数组中添加回调函数),startup(执行数组中的回调函数)等等
- 容器初始化时,需要根据状态创建不同模式的容器,如:stopOnFalse(如果某一个回调函数返回false时,是否终止执行)、memory(在添加新的回调函数时,原本的回调函数是否会被再次执行)、once(数组中回调函数是否只执行一次)等等标签
container的创建¶
container创建demo如下:
var cache = {};// 缓存对象
var container =function (flags){
flags = typeof flags =='string' ? (cache[flags] || createFlags(flags)): extend({},flags)
console.log(flags)
var carryOut,len,i,memory,stackLen, stackPoint;
var stack = []; // 用于存储回调列表
// 执行传递过来的回调函数数组
var fire = function (data) {
memory = flags.memory && data
len = stack.length
i = stackPoint || 0
stackPoint = 0
carryOut = true
for (;i<len;i++) {
if(stack[i].apply(data[0], data[1]) == false && flags.stopOnFalse){
//执行当前函数并且为其绑定执行上下文
break;
}
}
}
var self ={
// add函数的写法是一个立即执行函数IIFE,后面的(arguments)则是立即调用这个匿名函数,并将 arguments对象作为参数传递给它,其实arguments就是对应入参中的args
// (function(args) {
// 你可以在这里使用args访问传入的参数
// })(arguments);
add: function (){
(function add(args){
stackLen = stack.length
// Array.from(agrs)将传进来的数据转换为真正的数组进行处理
Array.from(args).forEach(function (arg){
// agr.toString()和toString.call(agr)在某种情况下是一样的效果,但是还是有一定 的不一样
// 1,首先agr.toString()需要保证agr本身有toString()这个方法,否则会有未知的错误
// 2,toString.call(agr)则更加的安全,即使 agr 是 null 或 undefined, Object.prototype.toString.call(null) 和 Object.prototype.toString.call(undefined) 也不会抛出错误,而是返回 " [object Null]" 和 "[object Undefined]"。
// if(agr.toString() ==='[Object Function]') {
// 成员是一个非数组,是一个函数的情况
if(toString.call(arg) ==='[object Function]') {
// 检测是否有重复的回调
if (!self.has(arg)){
stack.push(arg)
}
// 传进来的入参是数组的情况,就递归添加
} else if ( arg && arg.length && typeof arg !== 'string' ) {
add(arg)
}
})
})(arguments)// arguments是父级函数的入参
if (memory) {
stackPoint =stackLen
fire(memory)
}
}, // 向回调列表中添加回调函数
// context就是当前函数的执行上下文
startupWith:function (context, args){
args || [];
args = [context,args]
// 因为每次执行startup都是调用startupWith函数,且startupWith中主要是使用fire方法调用回调函数数组中的函数,所以只需控制是否执行fire函数即可
if (!flags.once || !carryOut) {
fire(args)
}
},
startup: function (){
self.startupWith(this, arguments)
// this就是self,arguments就是调用startup函数的入参
}, // 开始执行回调列表中的回调函数
has: function (fn){
return stack.indexOf(fn) > -1; // 大于-1说明有重复的值
}
};
return self;
}
function createFlags(flags){
var res = {};
// 用于在空白处将flags进行拆分,将flags根据空格进行拆分拆分为数组
(flags.match(/\S+/g)||[]).forEach(function (flag) {
res[flag] = true;
});
return res
}
function extend(to,from){
for (let key in from){
to[key] = from[key];
}
return to
}
Promise的创建¶
function Promise (func){
var tuples = [
// 状态 添加回调的方法(语法糖) 创建容器->回调列表 异步操作的最终的状态
['resolve','done',container('once memory'),'resolved'],
['reject','fail',container('stopOnFalse'),'rejected']
]
var state = 'pending' // 只要创建promise对象就是处于pending的状态
var chain = {
then: function (/* fnSuccess,fnFail*/) {
var args = arguments
return Promise(function (resolve, reject) {
tuples.forEach(function (tuple,index){
// 判断传递过来的resolve和reject是不是函数
var fn = toString.call(args[index]) === '[object Function]' && args[index]
chain[tuple[1]](function () {
var returned = fn && fn.apply(this, arguments)
if (returned && toString.call(returned.then)=='[object Function]') {
returned.done(resolve)
returned.fail(reject)
}
})
})
})
}
}
var protect = {}
tuples.forEach(function (tuple,index){
// list就是创建出来的容器
var list = tuple[2];
var statusString = tuple[3]
if(statusString) {
list.add(function () {
state = statusString
})
}
// chain [done / fail],通过chain.done chain.fail来向对应的对象中添加回调函数的
chain[tuple[1]] = list.add
// 状态绑定 resolve('xxxx') reject('xxxxx')
protect[tuple[0]] = function () {
protect[tuple[0]+'With'](this, arguments)
}
// resolveWith rejectWith
protect[tuple[0]+'With'] = list.startupWith
})
if(func){
func.call(chain, protect['resolve'], protect['reject'])
}
return chain
}