Promise运行流程
Promise有pending(进行中)、fulfilled(已完成)、rejected(已失败)三种状态。它们有两种状态转变过程:
- 第一,由pending状态到fulfilled,通过resolve进行处理
- 第二,由pending到rejected,通过reject进行处理。
由于resolve处理异步时传入的参数为实例本身时,程序也会抛出异常进行reject来结束程序执行。先看一下JavaScript的异常有哪几种类型
JavaScript的异常类型
- Error:最基本的错误类型。其他错误的类型都继承自该类型。(程序运行过程中抛出的异常一般都有具体类型,Error类型一般都是开发人员自己抛出的异常)
- SyntaxError:语法错误,也称解析错误。表示不符合编程语言的语法规范。JavaScript是一门解释性语言,代码执行时需要经历词法分析->语法分析->语法树。词法分析是将字符流(char stream)转换为记号流(token stream)、语法分析阶段会将记号流(token stream)生成抽象语法树(AST)。在这两个阶段如果JavaScript引擎发现了预期之外/无法转换的token或者token顺序和预期不一致时就会抛出SyntaxError。
- TypeError:类型错误。表示参数或变量不是预期的类型。
- ReferenceError:引用错误。引用一个不存在的变量发生的错误,每当创建或定义一个变量时,变量名都会写入一个变量储存中心,这个变量储存中心就像键值存储一样,每当引用变量时,它都会去存储中找到Key并提取返回Value,如果要找的变量不在存储中,就会抛出ReferenceError
- RangError:边界错误。表示超出有效范围时发生的异常
- URIError:URL错误。在调用URI相关的方法中URL无效时抛出的异常。主要包括encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()几个函数
Promise的源码实现
- Promise构造函数
function noop () {}
function Promise (fn) {
// 判断Promise是否new关键字进行实例化
if (!(this instanceof Promise)) {
throw new TypeError('Promise must be constructed via new')
}
// Promise在实例化时的参数必须是个函数
if (typeof fn !== 'function') {
throw new TypeError('Promise constructor\'s arguments is not a function')
}
// noop是个空函数,如果传入的fn为noop不做任何操作
if (fn === noop) return
// Promise 延迟执行状态
this._deferredState = 0
// Promise 执行状态
this._state = 0;
// Promise执行结果
this._value = null;
// 延迟处理存储(数组)
this._deferreds = null;
// 实现内部绑定
doResolve(fn, this)
}
复制代码
Promise构造函数内部主要是对Promise实例化方式、实例化时Promise传入参数进行验证。接下来是定义Promise内部的运行状态、运行结果等的变量。_deferredStatePromise内部应用了另外一个promise时的延迟执行状态;_statePromise实例本身运行状态;_valuePromise实例本身运行结果;_deferredsPromise内部调用异步处理任务时,存储延迟处理函数,当签Promise的resolve或reject被调用后才去执行这里面的方法。doResolve()方法主要实现实例化时Promise所传回调函数及函数参数resolve和reject的方法绑定。
- doResolve
var LAST_ERROR = null
var IS_ERROR = {}
// tryCallTwo将Promise的回调函数的两个参数转化为处理方法
function tryCallTwo(fn, a, b) {
try {
fn (a, b)
} catch (ex) {
LAST_ERROR = ex
return IS_ERROR
}
}
function doResolve (fn, promise) {
// 初始完成状态为false
var done = false;
// 将实例Promise时传入的resolve和reject的内部实现
var res = tryCallTwo(fn, function (value) {
// 如果已完成,停止执行
if (done) return;
// 未完成将done设为true,以待下次判断
done = true;
// 调用resolve处理方法
resolve(promise, value);
}, function (reason) { // reject内部实现
if (done) return;
done = true;
// 调用reject处理方法
reject(promise, reason);
});
// tryCallTwo方法抛出异常时,将done设为true,调用reject处理程序
if (!done && res === IS_ERROR) {
done = true;
reject(promise, LAST_ERROR);
}
}
复制代码
仿写以上代码
var LAST_ERROR = null
var IS_ERROR = {}
function noop () { }
function tryCallTwo (fn, a, b) {
try {
fn(a, b)
} catch (ex) {
LAST_ERROR = ex
return IS_ERROR
}
}
function doResolve (fn, promise) {
var done = false
var res = tryCallTwo(fn, function (value) {
if (done) return
done = true
// resolve(promise, value)
}, function (reason) {
if (done) return
done = true
})
if (!done && res === IS_ERROR) {
done = true
}
}
class MyPromise {
constructor(fn) {
if (typeof fn !== 'function') {
throw TypeError('Promise contructor\'s arguments is not a function')
}
if (fn === noop) return
this._state = 0
this._valule = null
this._deferredState = 0
this._deferreds = null
doResolve(fn, this)
}
}
复制代码
ES6中的类与函数声明的区别
- 函数声明可以被提升,而类声明与let声明类似,不能被提升;真正执行声明语句之前,它们会一直存在于临时死区中
- 类声明中的所有代码将自动运行在严格模式下,而且无法强行让代码脱离严格模式执行
- 自定义类型中,需要通过Object.defineProperty()方法手工指定某个方法为不可枚举;而在类中,所有方法都是不可枚举的
- 每个类都有一个名为[[Constructor]]的内部方法,通过关键字new调用那些不包含[[Constructor]]的方法会导致程序抛出错误
- 使用除关键字new以外的方式调用类的构造函数会导致程序抛出错误
- 在类中修改类名会导致程序报错
在es6的class类中,除了使用关键字new以外的方式调用类的构造函数都会报错,所以在MyPromise类中没有做this指向的判断
参考博客
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END