本文引用文档地址javascript
你们都知道 Promise 解决了回调地狱的问题,可是到底什么是回调地狱? 他到底哪儿have some wrong ? 是由于嵌套回调仍是,仍是回调执行顺序问题,仍是不够美观?html
来脑补一下,实现一个说话的需求,说完才能说下一句,那么很容易的写出下面这样的一段代码java
function say (word, callback) {
console.log(word)
setTimeout(() => {
callback&&callback()
},1000)
}
复制代码
ok! fine!! 那么如今要说话了node
say("first", function () {
say("second", function () {
say("third", function () {
console.log("end");
});
});
});
// first second third end
复制代码
在这个例子中的嵌套的问题仅仅是缩进的问题,而缩进除了会让代码变宽可能会形成读代码的一点不方便以外,并无什么其余的问题。若是仅仅是这样,为何不叫“缩进地狱”或“嵌套地狱”?git
把回调地狱彻底理解成缩进的问题是常见的对回调地狱的误解。要回到“回调地狱”这个词语上面来,它的重点就在于“回调”,而“回调”在JS中应用最多的场景固然就是异步编程了。程序员
因此,“回调地狱”所说的嵌套实际上是指异步的嵌套。它带来了两个问题:可读性的问题和信任问题es6
很容易找到一个这样执行顺序问题的问题github
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(new Date, i);
}, 1000);
}
console.log(new Date, i);
复制代码
很好,结果不那么重要,可是你总得想那么一会吧。想代码的执行顺序问题,那么你必须熟知异步代码的执行机制,还要能很快的分析出想要的结果,ajax
so 你能作到么。npm
好的,让咱们接着脑补一下,你是华夏某银行的少主,你的一个程序员写下了一段代码,调用了三方的ajax 扣款请求,可是因为三方库机制问题,调用了屡次。
那么,你能很好的信任回调么。还会不会有其余的信任问题发生呢。
so
...
实际问题在于控制权转交给了第三方,不可控的执行致使了咱们最头疼的信任问题,实际在开发中,咱们须要检查的状态更多,依赖更多。信任问题带来的成本,就大大致使了可读性下降,须要更多的代码来check。
虽然这些 fail 出现的几率并不高,可能在你编码的时候都没有关注到,可是实际上却须要不少臃肿的代码去强壮他,这就不够友好了。
实际上万能的开发者们也想了不少方法来解决信任问题
// 增长失败回调
function sendAjax(onSuccess,onFail) {
if(success) {
onSuccess&&onSuccess()
}else{
onFail&&onFail()
}
}
复制代码
// node error first
fs.stat(file, (err, stat) => {
if (err) {
doSomeThing()
} else {
doOtherThing()
}
})
复制代码
实际上这并无解决全部的问题,诸如回调屡次执行,复杂场景下的回调问题。
小总结 异步回调带来的问题主要集中在 可读性差/信任问题
首先咱们来看一下 PromiseA+ 规范的第一句话
An open standard for sound, interoperable JavaScript promises—by implementers, for implementers.
一个开放的标准,用于实施者对实施者的声音,可互操做的JavaScript保证。
A promise represents the eventual result of an asynchronous operation. The primary way of interacting with a promise is through its then method, which registers callbacks to receive either a promise’s eventual value or the reason why the promise cannot be fulfilled.
一个promise表示异步操做的最终结果。 与诺言互动的主要方式是经过then方法,该方法注册回调以接收诺言的最终值或诺言没法实现的缘由。
This specification details the behavior of the then method, providing an interoperable base which all Promises/A+ conformant promise implementations can be depended on to provide. As such, the specification should be considered very stable. Although the Promises/A+ organization may occasionally revise this specification with minor backward-compatible changes to address newly-discovered corner cases, we will integrate large or backward-incompatible changes only after careful consideration, discussion, and testing.
该规范详细说明了then方法的行为,提供了一个可互操做的基础,全部Promises / A +符合诺言的实现均可以依靠该基础来提供。 所以,该规范应被视为很是稳定。 尽管Promises / A +组织有时会经过向后兼容的微小更改来修订此规范,以解决新发现的极端状况,但只有通过仔细考虑,讨论和测试以后,咱们才会集成大型或向后不兼容的更改。
1.0
Terminology 概念术语promise
is an object or function with a then method whose behavior conforms to this specification.thenable
is an object or function that defines a then method.value
is any legal JavaScript value (including undefined, a thenable, or a promise).exception
is a value that is thrown using the throw statement.reason
is a value that indicates why a promise was rejected.2.0
Requirements 要求 (实现)对于建立一个可互操做的javascript保证的标准来讲,要解决的问题无非就是状态管理,注册回调事件,和可靠的承诺解决体系
**PromiseA+**规范实际上就从这三点出发,定义了三种标准。
那么咱们来开始写咱们的代码,基于es6 class
class Promise {
constructor(executor) {
// PromiseA+...
}
}
module.exports = Promise;
复制代码
2.1
Promise StatesA promise must be in one of three states: pending, fulfilled, or rejected.
一个Promise
必须处于pending``fulfilled``rejected
三种状态之间
2.1.1
When pending, a promise:
2.1.1.1
may transition to either the fulfilled or rejected state.2.1.2
When fulfilled, a promise:
2.1.2.1
must not transition to any other state.2.1.2.2
must have a value, which must not change.2.1.3
When rejected, a promise:
2.1.3.1
must not transition to any other state.2.1.3.2
must have a reason, which must not change.Here, “must not change” means immutable identity (i.e. ===), but does not imply deep immutability.
这里实际上定义了Promise
的状态转换关系,定义了
pending
状态,能够转换为其余两个状态so!!! do it
class Promise {
constructor(executor) {
// 定义初始化状态常量
this.PENDING = 'pending';//初始态
this.FULFILLED = 'fulfilled';//初始态
this.REJECTED = 'rejected';//初始态
// PromiseA+ 2.1.1.1
// 初始化状态
this.status = this.PENDING;
// PromiseA+ 2.1
// 定义缓存经过then注册的成功失败回调的数组,支持 then 方法注册多个回调
this.onResolveCallbacks = [];
this.onRejectCallbacks = [];
// 缓存this,避免this指向问题致使bug
const self = this;
// 定义成功失败方法,做为Promise 传入的函数体的参数
// 实现PromiseA+状态转换 定义成功失败参数
function reject(v) {
// Here, “must not change” means immutable identity (i.e. ===), but does not imply deep immutability.
const reason = v;
//PromiseA+ 2.1.3
if (self.status === self.PENDING) {
self.status = self.REJECTED;
self.value = reason;
self.onRejectCallbacks.forEach(item => item(self.value));
}
}
function resolve(value) {
//PromiseA+ 2.1.2
if (self.status === self.PENDING) {
self.status = self.FULFILLED;
self.value = value;
self.onResolveCallbacks.forEach(item => item(self.value));
}
}
// 开始执行函数体,捕获错误。执行报错则直接拒绝Promise
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
}
module.exports = Promise;
复制代码
new Promise
过程已经实现了
2.2
The then MethodA promise must provide a then method to access its current or eventual value or reason.
A promise’s then method accepts two arguments:
promise.then(onFulfilled, onRejected)
复制代码
那么在类上定义方法
class Promise {
constructor(executor) {
...
}
// PromiseA+ 2.2 // PromiseA+ 2.2.6
then(onFulfilled, onRejected) {
}
}
module.exports = Promise;
复制代码
2.2.1
Both onFulfilled and onRejected are optional arguments:
2.2.1.1
If onFulfilled is not a function, it must be ignored.2.2.1.2
If onRejected is not a function, it must be ignored.2.2.2
If onFulfilled is a function:
2.2.2.1
it must be called after promise is fulfilled, with promise’s value as its first argument.2.2.2.2
it must not be called before promise is fulfilled.2.2.2.3
it must not be called more than once.2.2.3
If onRejected is a function,
2.2.3.1
it must be called after promise is rejected, with promise’s reason as its first argument.2.2.3.2
it must not be called before promise is rejected.2.2.3.3
it must not be called more than once.
- 成功/失败回调必须是一个
funciton
,不是函数将被忽略成功/失败回调
必须在成功/失败
以后被调用,第一个参数必须是value/reason
成功/失败回调
不能调用屡次
2.2.4
onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1].
成功/失败回调
不能在Promise 平台代码执行完以前被调用,意义在于防止调用时还有回调没有被注册进来
2.2.5
onFulfilled and onRejected must be called as functions (i.e. with no this value). [3.2]
成功/失败回调
必须做为函数被调用,若是拿到的参数不是函数,忽略它,生成默认的同步执行函数,以相同的值执行后续回调
2.2.6
then may be called multiple times on the same promise.
2.2.6.1
If/when promise is fulfilled, all respective onFulfilled callbacks must execute in the order of their originating calls to then.2.2.6.2
If/when promise is rejected, all respective onRejected callbacks must execute in the order of their originating calls to then.
then
方法可能被同一个promise
调用屡次then
方法注册的成功/失败回调
必须被以注册的顺序执行
2.2.7
then must return a promise [3.3].
2.2.7.1
If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).2.2.7.2
If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.2.2.7.3
If onFulfilled is not a function and promise1 is fulfilled, promise2 must be fulfilled with the same value as promise1.2.2.7.4
If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason as promise1.promise2 = promise1.then(onFulfilled, onRejected);
复制代码
then
方法必须返回一个Promise instance
promise1
成功或则失败回调正确执行拿到一个返回值value x
运行Promise Resolution Procedure
解析程序[Resolve]](promise2, x)
promise1
拒绝throws an exception e
,就以相同的缘由拒绝promise2
promise1
的成功/失败回调
不是一个function
并且promise1
成功/失败时,promise2
必须以相同的value
/e
被成功或者拒绝
so!!! do it!!!
PromiseA+ 2.2.4
onFulfilled or onRejected must not be called until the execution context stack contains only platform code.
为了防止在平台代码执行完毕,彻底注册回调以前调用回调,采用宏任务setTimeout0
实现 ::: details 展开查看constructor
constructor(executor) {
this.PENDING = 'pending';//初始态
this.FULFILLED = 'fulfilled';//初始态
this.REJECTED = 'rejected';//初始态
//PromiseA+ 2.1.1.1
this.status = this.PENDING;
// PromiseA+ 2.1
this.onResolveCallbacks = [];
this.onRejectCallbacks = [];
const self = this;
function reject(v) {
const reason = v;
// PromiseA+ 2.2.4
setTimeout(() => {
//PromiseA+ 2.1.3
if (self.status === self.PENDING) {
self.status = self.REJECTED;
self.value = reason;
// console.dir(self);
// console.log('------self--------------------------------');
self.onRejectCallbacks.forEach(item => item(self.value));
}
});
}
function resolve(value) {
// PromiseA+ 2.2.4
setTimeout(() => {
//PromiseA+ 2.1.2
if (self.status === self.PENDING) {
self.status = self.FULFILLED;
self.value = value;
self.onResolveCallbacks.forEach(item => item(self.value));
}
});
}
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
复制代码
:::
class Promise {
constructor(executor) {
...
}
resolvePromise(promise2, x, resolve, reject) {
}
// PromiseA+ 2.2 // PromiseA+ 2.2.6
then(onFulfilled, onRejected) {
//缓存this
const self = this;
//PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x;
onRejected = typeof onRejected === 'function' ? onRejected : e => { throw e; };
let promise2;
function fulfillCallback(resolve, reject) {
// PromiseA+ 2.2.4
setTimeout(() => {
try {
const x = onFulfilled(self.value);
//PromiseA+ 2.2.7.1
self.resolvePromise(promise2, x, resolve, reject);
} catch (error) {
//PromiseA+ 2.2.7.2
reject(error);
}
});
}
function rejectCallback(resolve, reject) {
// PromiseA+ 2.2.4
setTimeout(() => {
try {
const e = onRejected(self.value);
//PromiseA+ 2.2.7.1
self.resolvePromise(promise2, e, resolve, reject);
} catch (error) {
//PromiseA+ 2.2.7.2
reject(error);
}
});
}
// PromiseA+ 2.2.2
if (self.status === self.FULFILLED) {
//PromiseA+ 2.2.7
return promise2 = new Promise((resolve, reject) => {
fulfillCallback(resolve, reject);
});
}
// PromiseA+ 2.2.3
if (self.status === self.REJECTED) {
//PromiseA+ 2.2.7
return promise2 = new Promise((resolve, reject) => {
rejectCallback(resolve, reject);
});
}
if (self.status === self.PENDING) {
//PromiseA+ 2.2.7
return promise2 = new Promise((resolve, reject) => {
self.onResolveCallbacks.push(() => {
fulfillCallback(resolve, reject);
});
self.onRejectCallbacks.push(() => {
rejectCallback(resolve, reject);
});
});
}
}
}
module.exports = Promise;
复制代码
so 2.2.7.1 是个什么鬼!!鬼!!鬼啊!!!!
接着看下去吧
2.3
The Promise Resolution Procedure::: danger 交互性 javascript 保证 If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
promise1
成功或则失败回调正确执行拿到一个返回值value x
运行Promise Resolution Procedure
解析程序[Resolve]](promise2, x)
:::
The promise resolution procedure is an abstract operation taking as input a promise and a value, which we denote as [[Resolve]](promise, x). If x is a thenable, it attempts to make promise adopt the state of x, under the assumption that x behaves at least somewhat like a promise. Otherwise, it fulfills promise with the value x.
This treatment of thenables allows promise implementations to interoperate, as long as they expose a Promises/A+-compliant then method. It also allows Promises/A+ implementations to “assimilate” nonconformant implementations with reasonable then methods.
promise resolution procedure
promise 解决程序其实是一种承诺实现的抽象,将promise
和值x
做为输入,表示为[[Resolve]](promise,x)
若是x是可能的,则在x的行为至少相似于承诺的假设下,尝试使承诺采用x的状态。 不然,它将以值x履行承诺。这种对可实现对象的处理使答应实现能够互操做,只要它们公开了符合Promises / A +的then方法便可。 它还容许Promises / A +实现使用合理的then方法“整合”不合格的实现。
想要运行 promise resolution procedure
, 须要遵循瞎下面的规范。
2.3.1
If promise and x refer to the same object, reject promise with a TypeError as the reason.若是
promise
和x
指向同一个对象,以一个TypeError
做为缘由拒绝promise
2.3.2
If x is a promise, adopt its state [3.4]:
2.3.2.1
If x is pending, promise must remain pending until x is fulfilled or rejected.2.3.2.2
If/when x is fulfilled, fulfill promise with the same value.2.3.2.3
If/when x is rejected, reject promise with the same reason.若是
x
是一个promise
(是本身的实例instanceof
) 采用下面的状态
- 若是
x
在pending
状态,promise2
必须保持pending
状态直到x
被fulfilled/rejected
- 若是
x
被fulfilled/rejected
,promise2
必须保持x
相同的value/reason
被fulfilled/rejected
2.3.3
Otherwise, if x is an object or function,
2.3.3.1
Let then be x.then. [3.5]
2.3.3.2
If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
2.3.3.3
If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where:
2.3.3.3.1
If/when resolvePromise is called with a value y, run [[Resolve]](promise, y).
2.3.3.3.2
If/when rejectPromise is called with a reason r, reject promise with r.
2.3.3.3.3
If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
2.3.3.3.4
If calling then throws an exception e,
2.3.3.3.4.1
If resolvePromise or rejectPromise have been called, ignore it.2.3.3.3.4.2
Otherwise, reject promise with e as the reason.2.3.3.4
If then is not a function, fulfill promise with x.
2.3.3.4
If x is not an object or function, fulfill promise with x.
当
x
是一个object
orfunction
此时多是一个同步的处理函数,也多是一个thenable
对象
- 访问
x
的then
属性 实际上该操做可能会报错,通常来讲访问一个对象的属性不会报错,可是若是该属性是一个getter
的时候,在执行getter
的时候可能会抛异常e
。此时应该以e
来拒绝peomise2
- 当
then
是一个function
,经过then.call(x)
调用它,同时给x
注册成功处理函数和失败处理函数,
- 当成功回调被执行并传入
y
的时候,运行[[Resolve]](promise, y)
继续解析。- 当失败回调被执行并传入
e
的时候,把e
做为reason
拒绝promise2
- 若是成功失败回调被屡次调用,那么第一次的调用将优先调用,其余的调用将被忽略,这里须要添加
called
标志是否被调用,在每次调用成功失败时校验,并调用时立马修改标志位状态- 若是x不是
function
对象那么以x
实现promise
- 若是x不是
thenable
对象那么以x
实现promise
解析程序实际上保证了promise
的可靠性,对thenable
对象状态的判断,循环解析,直到x
做为一个普通的不能在被解析的非thenable
才实现调用,对错误的处理也贯彻整个流程,并且保证了调用的惟一性。 这实现了那句可互操做的JavaScript保证。
那么,让咱们来一步一步的实现它吧
resolvePromise(promise2, x, resolve, reject) {
const self = this;
// PromiseA+ 2.3.1
if (promise2 === x) { return reject(new TypeError('循环引用')); }
// PromiseA+ 2.3.2
if (x instanceof Promise) {
if (x.status === self.PENDING) {
// PromiseA+ 2.3.2.1
x.then(function(y) { self.resolvePromise(promise2, y, resolve, reject); }, reject);
} else {
// PromiseA+ 2.3.2.2 /PromiseA+ 2.3.2.3
x.then(resolve, reject);
}
// PromiseA+ 2.3.3
} else if (x && ((typeof x === 'object') || (typeof x === 'function'))) {
// PromiseA+ 2.3.3.3.3 / PromiseA+ 2.3.3.3.4.1
let called = false;
try {
// PromiseA+ 2.3.3.1
const then = x.then;
// PromiseA+ 2.3.3.3
if (typeof then === 'function') {
try {
then.call(
x,
function(y) {
if (called) return;
called = true;
// PromiseA+ 2.3.3.3.1
self.resolvePromise(promise2, y, resolve, reject);
},
function(e) {
if (called) return;
called = true;
// PromiseA+ 2.3.3.3.2
reject(e);
}
);
} catch (e) {
if (called) return;
called = true;
// PromiseA+ 2.3.3.3.2
reject(e);
}
} else {
// PromiseA+ 2.3.3.4
resolve(x);
}
} catch (error) {
if (called) return;
called = true;
// PromiseA+ 2.3.3.2 / PromiseA+ 2.3.3.4.2
reject(error);
}
} else {
// PromiseA+ 2.3.4
resolve(x);
}
}
复制代码
到此PromiseA+
规范已经彻底实现,原则上全部的Promise
库都应该遵循此规范,现代浏览器支持的promise
都支持一些Promise.all
,Promise.race
,Promise.resolve
,Promise.reject
,接下来让咱们来实现它
//all 实现
Promise.all = function(promises) {
//promises是一个promise的数组
return new Promise(function (resolve, reject) {
const arr = []; //arr是最终返回值的结果
let i = 0; // 表示成功了多少次
function processData(index, y) {
arr[index] = y;
if (++i === promises.length) {
resolve(arr);
}
}
for (let i = 0; i < promises.length; i++) {
promises[i].then(function (y) {
processData(i, y);
}, reject);
}
});
};
// race 实现
Promise.race = function (promises) {
return new Promise(function (resolve, reject) {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject);
}
});
};
// Promise.resolve 实现
Promise.resolve = function (value) {
return new Promise(function(resolve, reject) {
resolve(value);
});
};
// Promise.reject 实现
Promise.reject = function (reason) {
return new Promise(function(resolve, reject) {
reject(reason);
});
};
复制代码
npm install promises-aplus-tests -g
复制代码
测试套件其实是一个cli命令行工具,只须要在Promise 上暴露出一个fucntion
接口deferred
,函数返回一个对象,对象包含一个Promise 实例,和实例的resolve,reject 参数
Adapters In order to test your promise library, you must expose a very minimal adapter interface. These are written as Node.js modules with a few well-known exports:
Promise.deferred = function () {
const defer = {};
defer.promise = new Promise(function (resolve, reject) {
defer.resolve = resolve;
defer.reject = reject;
});
return defer;
};
复制代码
进入到Promise.js
所在目录,运行
promises-aplus-tests ./Promise.js
复制代码
or in package.json
"scripts": {
"testa:promise": "promises-aplus-tests ./src/promise/Promise.js"
},
复制代码
那么顺利的话! 你讲看到这个