[实践系列] 主要是让咱们经过实践去加深对一些原理的理解。前端
实践系列-前端路由 git
实践系列-Babel原理 github
有兴趣的同窗能够关注 实践系列 。 求star求follow~web
Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一
为实现者提供一个健全的、可互操做的 JavaScript promise 的开放标准。
下面咱们先来说述Promise/A+ 规范的几个基本要求。
一个Promise的当前状态必须是如下三种状态中的一种: 等待状态(Pending) 执行状态(Fulfilled) 和 拒绝状态(Rejected)。算法
const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected';
等待状态 (Pending)npm
处于等待态时,promise 需知足如下条件:编程
if (this.state === PENDING) { this.state = FULFILLED || REJECTED ; }
执行状态 (Fulfilled)json
处于执行态时,promise 需知足如下条件:segmentfault
this.value = value;
拒绝状态 (Rejected) promise
处于拒绝态时,promise 需知足如下条件:
this.reason = reason;
这里的不可变指的是恒等(便可用 === 判断相等),而不是意味着更深层次的不可变(译者注:盖指当 value 或 reason 不是基本值时,只要求其引用地址相等,但属性值可被更改)
一个 promise 必须提供一个 then 方法以访问其当前值、终值和据因。
promise 的 then 方法接受两个参数:
promise.then(onFulfilled, onRejected)
参数可选
onFulfilled 和 onRejected 都是可选参数。
onFulfilled 特性
若是 onFulfilled 是函数:
onRejected 特性
若是 onRejected 是函数:
调用时机
onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用 注1
注1 这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环以后的新执行栈中执行。
这个事件队列能够采用“宏任务(macro - task)”机制或者“微任务(micro - task)”机制来实现。
因为 promise 的实施代码自己就是平台代码(译者注:即都是 JavaScript),故代码自身在处理在处理程序时可能已经包含一个任务调度队列。
调用要求
onFulfilled 和 onRejected 必须被做为函数调用(即没有 this 值)
屡次调用
then 方法能够被同一个 promise 调用屡次
咱们先经过实践一个简易版的Promise来消化一下上面Promises/A+规范的基本要求。
首先
npm init // 测试实现是否符合 promises/A+ 规范 npm install promises-aplus-tests -D
package.json
{ "name": "ajpromise", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "promises-aplus-tests ./simple.js" }, "author": "webfansplz", "license": "MIT", "devDependencies": { "promises-aplus-tests": "^2.1.2" } }
simple.js
//Promise 的三种状态 (知足要求 -> Promise的状态) const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; class AjPromise { constructor(fn) { //当前状态 this.state = PENDING; //终值 this.value = null; //拒因 this.reason = null; //成功态回调队列 this.onFulfilledCallbacks = []; //拒绝态回调队列 this.onRejectedCallbacks = []; //成功态回调 const resolve = value => { // 使用macro-task机制(setTimeout),确保onFulfilled异步执行,且在 then 方法被调用的那一轮事件循环以后的新执行栈中执行。 setTimeout(() => { if (this.state === PENDING) { // pending(等待态)迁移至 fulfilled(执行态),保证调用次数不超过一次。 this.state = FULFILLED; // 终值 this.value = value; this.onFulfilledCallbacks.map(cb => { this.value = cb(this.value); }); } }); }; //拒绝态回调 const reject = reason => { // 使用macro-task机制(setTimeout),确保onRejected异步执行,且在 then 方法被调用的那一轮事件循环以后的新执行栈中执行。 (知足要求 -> 调用时机) setTimeout(() => { if (this.state === PENDING) { // pending(等待态)迁移至 fulfilled(拒绝态),保证调用次数不超过一次。 this.state = REJECTED; //拒因 this.reason = reason; this.onRejectedCallbacks.map(cb => { this.reason = cb(this.reason); }); } }); }; try { //执行promise fn(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { typeof onFulfilled === 'function' && this.onFulfilledCallbacks.push(onFulfilled); typeof onRejected === 'function' && this.onRejectedCallbacks.push(onRejected); // 返回this支持then 方法能够被同一个 promise 调用屡次 return this; } }
就这样,一个简单的promise就完成了.
new AjPromise((resolve, reject) => { setTimeout(() => { resolve(2); }, 2000); }) .then(res => { console.log(res); return res + 1; }) .then(res => { console.log(res); }); //output // delay 2s.. // 2 // 3
接下来,咱们来看看咱们的实现是否彻底符合promises/A+规范~
npm run test
GG,测试用例只过了一小部分,大部分飘红~
OK,接下来,咱们来继续了解promises/A+ 进一步的规范要求~
因为接下来的要求比较抽象和难理解,因此咱们将一步一步实践来加深理解。
咱们经过以上要求来一步一步完善then方法
1.
// 1.首先,then方法必须返回一个promise对象 then(onFulfilled, onRejected) { let newPromise; return (newPromise = new AjPromise((resolve, reject) => {})); }
2.
then(onFulfilled, onRejected) { let newPromise; return (newPromise = new AjPromise((resolve, reject) => { // 2.若是 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x) this.onFulfilledCallbacks.push(value => { let x = onFulfilled(value); //解决过程 resolvePromise resolvePromise(newPromise, x); }); this.onRejectedCallbacks.push(reason => { let x = onRejected(reason); //解决过程 resolvePromise resolvePromise(newPromise, x); }); })); } // 解决过程 function resolvePromise() { //... }
3.
then(onFulfilled, onRejected) { let newPromise; return (newPromise = new AjPromise((resolve, reject) => { // 3.若是 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e。 this.onFulfilledCallbacks.push(value => { try { let x = onFulfilled(value); resolvePromise(newPromise, x); } catch (e) { reject(e); } }); this.onRejectedCallbacks.push(reason => { try { let x = onRejected(reason); resolvePromise(newPromise, x); } catch (e) { reject(e); } }); })); }
4,5.
then(onFulfilled, onRejected) { let newPromise; // 4.若是 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值。 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; // 5.若是 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因。 onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; }; return (newPromise = new AjPromise((resolve, reject) => { this.onFulfilledCallbacks.push(value => { try { let x = onFulfilled(value); resolvePromise(newPromise, x); } catch (e) { reject(e); } }); this.onRejectedCallbacks.push(reason => { try { let x = onRejected(reason); resolvePromise(newPromise, x); } catch (e) { reject(e); } }); })); }
6.
then(onFulfilled, onRejected) { let newPromise; onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; }; // 2.2.6规范 对于一个promise,它的then方法能够调用屡次. // 当在其余程序中屡次调用同一个promise的then时 因为以前状态已经为FULFILLED / REJECTED状态,则会走如下逻辑, // 因此要确保为FULFILLED / REJECTED状态后 也要异步执行onFulfilled / onRejected ,这里使用setTimeout // 6.不论 promise1 被 reject 仍是被 resolve 时 promise2 都会被 resolve,只有出现异常时才会被 rejected。 // 因为在接下来的解决过程当中须要调用resolve,reject进行处理,处理咱们在调用处理过程时,传入参数 if (this.state == FULFILLED) { return (newPromise = new AjPromise((resolve, reject) => { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(newPromise, x, resolve, reject); } catch (e) { reject(e); } }); })); } if (this.state == REJECTED) { return (newPromise = new AjPromise((resolve, reject) => { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(newPromise, x, resolve, reject); } catch (e) { reject(e); } }); })); } if (this.state === PENDING) { return (newPromise = new AjPromise((resolve, reject) => { this.onFulfilledCallbacks.push(value => { try { let x = onFulfilled(value); resolvePromise(newPromise, x, resolve, reject); } catch (e) { reject(e); } }); this.onRejectedCallbacks.push(reason => { try { let x = onRejected(reason); resolvePromise(newPromise, x, resolve, reject); } catch (e) { reject(e); } }); })); } }
ok,完整的then方法搞定了。相信经过以上实践,你对返回要求已经有了更深的理解。
Promise 解决过程是一个抽象的操做,其需输入一个 promise 和一个值,咱们表示为 [[Resolve]](promise, x),若是 x 有 then 方法且看上去像一个 Promise ,解决程序即尝试使 promise 接受 x 的状态;不然其用 x 的值来执行 promise 。这种 thenable 的特性使得 Promise 的实现更具备通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法便可;这同时也使遵循 Promise/A+ 规范的实现能够与那些不太规范但可用的实现能良好共存。
运行 [[Resolve]](promise, x) 需遵循如下步骤:
1。x 与 promise 相等
若是 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise。
2。x 为 Promise
3。x 为对象或函数
若是 x 为对象或者函数:
若是 then 是函数,将 x 做为函数的做用域 this 调用之。传递两个回调函数做为参数,第一个参数叫作 resolvePromise ,第二个参数叫作 rejectPromise:
若是调用 then 方法抛出了异常 e:
若是一个 promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为据因来拒绝 promise 。
1.x 与 promise 相等
function resolvePromise(promise2, x, resolve, reject) { //x 与 promise 相等 //若是从onFulfilled中返回的x 就是promise2 就会致使循环引用报错 //若是 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise if (x === promise2) { reject(new TypeError('循环引用')); } }
2.x 为 Promise。
function resolvePromise(promise2, x, resolve, reject) { if (x === promise2) { reject(new TypeError('循环引用')); } // x 为 Promise else if (x instanceof AjPromise) { // 若是 x 为 Promise ,则使 promise 接受 x 的状态 // 若是 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝 if (x.state === PENDING) { x.then( y => { resolvePromise(promise2, y, resolve, reject); }, reason => { reject(reason); } ); } else { // 若是 x 处于执行态,用相同的值执行 promise // 若是 x 处于拒绝态,用相同的据因拒绝 promise x.then(resolve, reject); } } }
3.x 为对象或函数
function resolvePromise(promise2, x, resolve, reject) { if (x === promise2) { reject(new TypeError('循环引用')); } if (x instanceof AjPromise) { if (x.state === PENDING) { x.then( y => { resolvePromise(promise2, y, resolve, reject); }, reason => { reject(reason); } ); } else { x.then(resolve, reject); } } else if (x && (typeof x === 'function' || typeof x === 'object')) { // 避免屡次调用 let called = false; try { //把 x.then 赋值给 then let then = x.then; if (typeof then === 'function') { // 若是 then 是函数,将 x 做为函数的做用域 this 调用之。 // 传递两个回调函数做为参数,第一个参数叫作 resolvePromise ,第二个参数叫作 rejectPromise // 若是 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了屡次,则优先采用首次调用并忽略剩下的调用 then.call( x, // 若是 resolvePromise 以值 y 为参数被调用,则运行[[Resolve]](promise, y) y => { if (called) return; called = true; resolvePromise(promise2, y, resolve, reject); }, // 若是 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise r => { if (called) return; called = true; reject(r); } ); }else { // 若是 then 不是函数,以 x 为参数执行 promise resolve(x); } } catch (e) { // 若是取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise // 若是调用 then 方法抛出了异常 e: // 若是 resolvePromise 或 rejectPromise 已经被调用,则忽略之 // 不然以 e 为据因拒绝 promise if (called) return; called = true; reject(e); } } else { // 若是 x 不为对象或者函数,以 x 为参数执行 promise resolve(x); } }
Ok~比较复杂的解决过程也让咱们搞定了.接下来咱们整合下代码
const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; class AjPromise { constructor(fn) { this.state = PENDING; this.value = null; this.reason = null; this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = value => { if (value instanceof Promise) { return value.then(resolve, reject); } setTimeout(() => { if (this.state === PENDING) { this.state = FULFILLED; this.value = value; this.onFulfilledCallbacks.map(cb => { cb = cb(this.value); }); } }); }; const reject = reason => { setTimeout(() => { if (this.state === PENDING) { this.state = REJECTED; this.reason = reason; this.onRejectedCallbacks.map(cb => { cb = cb(this.reason); }); } }); }; try { fn(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { let newPromise; onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; }; if (this.state === FULFILLED) { return (newPromise = new AjPromise((resolve, reject) => { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(newPromise, x, resolve, reject); } catch (e) { reject(e); } }); })); } if (this.state === REJECTED) { return (newPromise = new AjPromise((resolve, reject) => { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(newPromise, x, resolve, reject); } catch (e) { reject(e); } }); })); } if (this.state === PENDING) { return (newPromise = new AjPromise((resolve, reject) => { this.onFulfilledCallbacks.push(value => { try { let x = onFulfilled(value); resolvePromise(newPromise, x, resolve, reject); } catch (e) { reject(e); } }); this.onRejectedCallbacks.push(reason => { try { let x = onRejected(reason); resolvePromise(newPromise, x, resolve, reject); } catch (e) { reject(e); } }); })); } } } function resolvePromise(promise2, x, resolve, reject) { if (x === promise2) { reject(new TypeError('循环引用')); } if (x instanceof AjPromise) { if (x.state === PENDING) { x.then( y => { resolvePromise(promise2, y, resolve, reject); }, reason => { reject(reason); } ); } else { x.then(resolve, reject); } } else if (x && (typeof x === 'function' || typeof x === 'object')) { let called = false; try { let then = x.then; if (typeof then === 'function') { then.call( x, y => { if (called) return; called = true; resolvePromise(promise2, y, resolve, reject); }, r => { if (called) return; called = true; reject(r); } ); } else { resolve(x); } } catch (e) { if (called) return; called = true; reject(e); } } else { resolve(x); } } AjPromise.deferred = function() { let defer = {}; defer.promise = new AjPromise((resolve, reject) => { defer.resolve = resolve; defer.reject = reject; }); return defer; }; module.exports = AjPromise;
再来看看咱们的实现是否符合Promises/A+规范
npm run test
nice,测试用例所有经过!
若是以为有帮助到你,请给个star支持下做者~