一、背景
promiz是一个Promise的一个库,体积较小,官方介绍大概913 bytes,里面具体介绍了promise是如何实现的,主要是以ES5的一些语法来实现的
二、代码浅析
很多类库都以这种方式来写,都写在一个闭包里(比如jQuery)
好处:把promiz中的方法和变量保护起来,为了防止全局变量污染;
劣处:它会产生不释放的栈内存,过多使用容易导致内存溢出或者降低性能
下面是promiz的大致结构
(function(){
global = this
// 变量声明
var queueId = 1
var queue = {}
var isRunningTask = false
//异步执行器
function nextTick(){
// xxxx
}
//构造函数
function Deferred(resolver){
// 变量
// 方法:resolve、reject、then、catch
// 一些工具方法:thennable、fire
}
//对外暴露API
if (typeof module != 'undefined') {
module['exports'] = Deferred
} else {
global['Promise'] = global['Promise'] || Deferred
}
})()
具体看下这些方法是怎么实现的呢?
1、异步执行器
说起异步执行,我们通常想到的大概就这四个吧,但使用场景各不同吧
- setTimeOut()
- setInterval()
- setImmediate()
- process.nextTik()
前三个可以归为宏任务,第四个为微任务,微任务的执行优先级是高于宏任务的,在promiz中的异步执行具体实现如下
if (!global.setImmediate)
global.addEventListener('message', function (e) {
if (e.source == global){
if (isRunningTask)
nextTick(queue[e.data])
else {
isRunningTask = true
try {
queue[e.data]()
} catch (e) {}
delete queue[e.data]
isRunningTask = false
}
}
})
function nextTick(fn) {
if (global.setImmediate) setImmediate(fn)
// if inside of web worker
else if (global.importScripts) setTimeout(fn)
else {
queueId++
queue[queueId] = fn
global.postMessage(queueId, '*')
}
}
下面是构造函数
首先进来会有类型判断,判断传入的resolver是否是函数并且不是undefined,resolve和reject两个方法中均调用异步执行,在调用之前会有一个判断,看到当前状态是否改变,如果改变了就不执行了,没改变就执行,这也就是promise中的pending状态只能变成成功或者失败状态后,该状态就不可改变了;
then方法中 d = new Deferred(),然后返回d,就是每次then方法执行都会返回一个新的promise,then中会判断当前状态是3时就直接执行resolve方法,当前状态是4时就直接执行reject方法
function Deferred(resolver){
'use strict'
if (typeof resolver != 'function' && resolver != undefined)
throw TypeError()
if (typeof this != 'object' || (this && this.then))
throw TypeError()
// states
// 0: pending
// 1: resolving
// 2: rejecting
// 3: resolved
// 4: rejected
var self = this,
state = 0, // promise状态
val = 0, // success callback返回值
next = [], // 返回的新的promise对象
fn, er; // then方法中的成功回调函数和失败回调函数
self['promise'] = self
self['resolve'] = function (v) {
fn = self.fn
er = self.er
if (!state) {
val = v
state = 1
nextTick(fire)
}
return self
}
self['reject'] = function (v) {
fn = self.fn
er = self.er
if (!state) {
val = v
state = 2
nextTick(fire)
}
return self
}
self['_d'] = 1
self['then'] = function (_fn, _er) {
if (!(this._d == 1))
throw TypeError()
var d = new Deferred()
d.fn = _fn
d.er = _er
if (state == 3) {
d.resolve(val)
}
else if (state == 4) {
d.reject(val)
}
else {
next.push(d)
}
return d
}
self['catch'] = function (_er) {
return self['then'](null, _er)
}
}
在上面的构造函数中我们看到了改变状态会异步触发一个fire函数,下面看一下fire函数是干什么的
function fire() {
// check if it's a thenable
var ref;
try {
ref = val && val.then
} catch (e) {
val = e
state = 2
return fire()
}
thennable(ref, function () {
state = 1
fire()
}, function () {
state = 2
fire()
}, function () {
try {
if (state == 1 && typeof fn == 'function') {
val = fn(val)
}
else if (state == 2 && typeof er == 'function') {
val = er(val)
state = 1
}
} catch (e) {
val = e
return finish()
}
if (val == self) {
val = TypeError()
finish()
} else thennable(ref, function () {
finish(3)
}, finish, function () {
finish(state == 1 && 3)
})
})
}
fire中判断成功时callback的返回值是否是一个promise,并且执行thennable函数,然后传递了3个回调函数,那么接下来看下thennable是干啥的呢
// ref : reference to 'then' function
// cb, ec, cn : successCallback, failureCallback, notThennableCallback
function thennable (ref, cb, ec, cn) {
// Promises can be rejected with other promises, which should pass through
if (state == 2) {
return cn()
}
if ((typeof val == 'object' || typeof val == 'function') && typeof ref == 'function') {
try {
// cnt protects against abuse calls from spec checker
var cnt = 0
ref.call(val, function (v) {
if (cnt++) return
val = v
cb()
}, function (v) {
if (cnt++) return
val = v
ec()
})
} catch (e) {
val = e
ec()
}
} else {
cn()
}
};
在thenable中,会先判断当前状态是2就执行cn;如果ref不是一个函数时同样也会执行cn;
三、总结
promiz库代码量少,但实现方式算是独特吧,短小精悍
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。