Promise 是两个杰出的触发器软件系统,那时非主流应用程序上都早已同时实现了对Promise的全力支持,他们将先介绍Promise/A+的规范化,后再手撕同时实现Promise的核心理念标识符。
Promise/A+ 规范化
具体来说他们先看一看Promise/A+规范化什么样文本
名词
1.1 promise 是两个暗含合乎此规范化的then方式的第一类或是表达式。
1.2 thenable 是两个表述了两个then方式的第一类或是表达式。
1.3 value 是两个任一不合法的JavaScript值(主要包括undefined, thenable,或是promise)。
1.4 exception 是两个采用throw句子放出的值。
1.5 reason 是两个表示为何promise被rejected的值。2. 明确要求
2.1 promise 状况
两个promise要是四种状况当中的一类状况:pending,fulfilled 或是 rejected。
2.1.1 当promise处在pending状况时:
2.1.1.1 能变革到 fulfilled 或是 rejected 状况。
2.1.2 当promise处在fulfilled状况时:
2.1.2.1 很大无法变革到其它任何人一类状况。
2.1.2.2 要有两个value,因此那个值很大无法发生改变。
2.1.3 当promise处在rejected状况时:
2.1.3.1 很大无法变革到其它任何人一类状况。
2.1.3.2 要有两个reason,因此那个值无法发生改变。
在这里,“很大无法发生改变”意味着不可变的身份(即===),但并不意味着深层不变性,例如value/reason指向的地址是不可变的,但假若value/reason为两个第一类,则第一类内的值是可变的。
2.2 then 方式
两个promise要提供两个then方式去访问当前或是最终的value或是reason。
两个promise的then方式接受两个参数:
promise.then(onFulfilled, onRejected)
2.2.1 onFulfilled 和 onRejected 都是可选的参数:
2.2.1.1 如果 onFulfilled 不是两个表达式,它要被忽略。
2.2.1.2 如果 onRejected 不是两个表达式,它要被忽略。
2.2.2 如果 onFulfilled 是两个表达式:
2.2.2.1 promise 是 fulfilled 状况时它要被调用,因此 promise 的 value 作为它的第两个参数。
2.2.2.2 它很大无法在 promise 进入 fulfilled 状况之前被调用。
2.2.2.3 它很大无法够调用超过一次。
2.2.3 如果 onRejected 时两个表达式:
2.2.3.1 promise 是 rejected 状况时它要被调用,因此 promise 的 reason 作为它的第两个参数。
2.2.3.2 它很大无法在 promise 进入 rejected 状况之前被调用。
2.2.3.3 它很大无法调用超过一次。
2.2.4 直到执行上下文堆栈仅包含平台标识符之前,onFulfilled 或 onRejected 无法被调用。[3.1]
2.2.5 onFulfilled 和 onRejected 要以表达式的形式被调用(即没有this值)。[3.2]
2.2.6 then 能在同两个promise被调用多次:
2.2.6.1 当 promise 处在 fulfilled 状况时,各个 onFulfilled 回调要按其原始调用的顺序执行。
2.2.6.2 当 promise 处在 rejected 状况时,各个 onRejected 回调要按其原始调用的顺序执行。
2.2.7 then 要返回两个promise [3.3]:
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1 如果 onFulfilled 或 onRejected 返回两个值 x,运行Promise解决程序 [[Resolve]](promise2, x)。
2.2.7.2 如果 onFulfilled 或 onRejected 放出两个意外 e,promise2 要以 e 为 reason 被 rejected。
2.2.7.3 如果 onFulfilled 不是两个表达式因此 promise1 处在 fulfilled 状况,promise2 要以与 promise1 同样的 value
变革到 fulfilled 状况。
2.2.7.4 如果 onRejected 不是两个表达式因此 promise1 处在 rejected状况,promise2 要以与 promise1 同样的 reason
变革到 rejected状况。
2.3 Promise解决程序
promise解决程序是两个抽象的操作,它把两个 promise 和一个 value 作为输入,他们将那个表示为 [[Resolve]](promise, x)。如果 x 是两个 thenable ,它将会试图让 promise 采用 x 的状况,前提是x的行为至少有点像两个 promise。否则,它将会用值 x 执行 promise。
对这些 thenable 的处理使得与 promise 同时实现方式能去互相操作。只要它们公开了合乎 Promise/A+ 的 then 方式。它还使得 promises/A+ 同时实现方式能采用合理的 then 方式去“同化”不一致的同时实现方式。
为了运行[[Resolve]](promise, x),执行以下步骤:
2.3.1 如果 promise 与 x 是同两个第一类,以 Tyeperror 作为 reason 去 reject promise。
2.3.2 如果 x 是两个 promise,采用它的状况:
2.3.2.1 如果 x 处在 pending 状况,promise 要保持 pending 状况直到 x 处在 fulfilled 或是 rejected 状况。
2.3.2.2 如果 x 处在 fulfilled 状况,以相同的 value 去 fulfill promise。
2.3.2.3 如果 x 处在 rejected 状况,以相同的 reason 去 reject promise。
2.3.3 否则,如果 x 是两个第一类或是表达式:
2.3.3.1 让 then 作为 x.then。
2.3.3.2 如果取属性 x.then 会导致放出异常 e,则以 e 为 reason reject promise。
2.3.3.3 如果 then 是两个表达式,让 x 作为 this 调用它,第两个参数为 resolvePromise,第二个参数为 rejectPromise,然后:
2.3.3.3.1 如果采用value y 调用
resolvepromise时,运行[[Resolve]](promise, y)。
2.3.3.3.2 如果采用reason r 调用 rejectPromise 时,也用 r reject promise。
2.3.3.3.3 如果 resolvePromise 和 rejectPromise 都被调用了,或多次调用同一参数,那么第两个调用优先,其它的调用都会被忽略。
2.3.3.3.4 如果调用 then 的过程中放出了两个意外 e,
2.3.3.3.4.1 如果 resolvePromise 或是 rejectPromise 被调用了,那么忽略它。
2.3.3.3.4.2 否则,把 e 作为 reason reject promise。
2.3.3.4 如果 then 不是两个表达式,将 x 作为参数执行 promise。
2.3.4 如果 x 不是两个第一类或是表达式,将 x 作为参数执行 promise。如果两个参与了 thenable循环链的 thenable 去 resolve promise,这样 [[Resolve]](promise, thenable) 的递归性质最终会导致 [[Resolve]](promise, thenable) 会被再次调用,遵循上述算法将会导致无限递归。他们鼓励去同时实现(但不是必需的)检测这样的递归,并以 TypeError 作为 reason 去 reject Promise。[3.6]
注意
3.1 这里的“平台标识符”指的是引擎,环境和 promise 同时实现标识符。实际上,那个明确要求保证了 onFulfilled 和 onRejected 将会触发器执行,在事件循环后,用两个新的堆栈来调用它。 这能通过“宏任务”机制(如 settimeou t或 setimmediate )或“微任务”机制(如 mutationobserver
或 process.nextick)来同时实现。由于 Promise 同时实现被视为平台标识符,因此它本身可能包含两个任务调度队列或“trampoline”,并在当中调用处理程序。
3.2 也就是说,在 strict 模式下,这(指的是this)在它们内部将会是 undefined;在 sloppy 模式下,它将会是全局第一类。
3.3 如果同时实现满足所有明确要求,则同时实现可能允许 promise2 == promise1。每个同时实现都应该记录它是否能生成 promise2 == promise1 以及在什么条件下。
3.4 一般来说,只有当 X 来自当前的同时实现时,才知道它是两个真正的 promise。本条款允许采用特定于同时实现的方式来采用已知一致承诺的状况。
3.5 此过程具体来说存储对 x 的引用,然后测试该引用,然后调用该引用,避免多次访问 x.then 属性。这些预防措施对于确保访问器属性的一致性非常重要,访问器属性的值可能在两次检索之间发生更改。
3.6 实现方式中不应当在 thenbale 链中的深度设置主观的限制,因此不应当假设链的深度超过主观的限制后会是无限的。只有真正的循环才能导致 TypeError。如果遇到由无限多个不同 thenable 组成的链,那么永远递归是正确的行为。
同时实现
promise.js
function promise(fuc){ //接收两个表达式作为参数
let that = this;
that.value = null; // 2.1.2.2 that.reason = null;// 2.1.3.2
that.onFulfilled = []; // 2.2.6
that.onRejected = []; //2.2.6
that.status = pending; // 2.1
function resolve(val){
if (that.status === pending)
{
// 2.1.2
that.status = fulfilled;
that.value = val;
that.onFulfilled.forEach(fc => fc(val)); //2.2.6.1
}
}
function reject(err){
if (that.status === pending)
{
//2.1.3 that.status = rejected;
that.reason = err;
that.onRejected.forEach(fc => fc(err)); //2.2.6.2
}
}
try {
fuc(resolve,reject);
} catch (error) {
reject(error);
}
}
promise.prototype.then = function (onFulfilled, onRejected) //2.2{
let that = this,
promise2; //2.2.7
onFulfilled = typeof onFulfilled === function ? onFulfilled : (x) => x; //2.2.1 2.2.7.3 onRejected = typeof onRejected === function ? onRejected : (e) => { throw e }; // 2.2.7.4
switch (that.status) {
case pending:
promise2 = new promise((reslove, reject)=>{
that.onFulfilled.push(()=>{
setTimeout(() => {
try {
let x = onFulfilled(that.value); //2.2.2.1
ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1
} catch (err) {
reject(err); //2.2.7.2
}
}, 0);
});
that.onRejected.push(()=>{
setTimeout(() => {
try {
let x = onRejected(that.reason); //2.2.3.1
ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1
} catch (err) {
reject(err); //2.2.7.2 }
}, 0);
});
})
break;
case fulfilled:
promise2 = new promise((reslove,reject)=>{
setTimeout(() => { // 2.2.4 try{
let x = onFulfilled(that.value); //2.2.2.1
ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1
}catch(err){
reject(err); //2.2.7.2
}
}, 0);
})
break;
case rejected:
promise2 = new promise((reslove, reject)=>{
setTimeout(() => { // 2.2.4
try{
let x = onRejected(that.reason); //2.2.3.1
ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1
}catch(err){
reject(err); //2.2.7.2
}
}, 0);
})
break;
default:
break;
}
return promise2;
}
function ResolutionProcedure(promise1, x, reslove, reject){ //2.3
if (promise1 === x) //2.3.1
return reject(new TypeError(“same promise”));
if (x instanceof Promise) //2.3.2
x.then(((xvalue)=>{
ResolutionProcedure(promise1, xvalue, reslove, reject);
},(xreason)=>{
reject(xreason)
}));
if (x !== null &&( typeof x === object || typeof x === function )) //2.3.3
{
try {
let then = x.then;//2.3.3.1
if(typeof then === function) //2.3.3.3
{
let sign = false;
try {
then.call(x,(y)=>{
if (!sign)
{
ResolutionProcedure(promise1, y, reslove, reject); //2.3.3.3.1
sign = true;
}
},(r)=>{
if (!sign) {
reject(r) //2.3.3.3.2
sign = true;
}
})
} catch (error) {
if(!sign)
reject(error) //2.3.3.3.4.2
}
}else{
reslove(x); //2.3.3.4
}
} catch (error) {
reject(error); //2.3.3.2
}
}else{
reslove(x); //2.3.4
}
}
promise.resolve = function(value) {
return new promise(resolve => resolve(value))
}
promise.reject = function (reason) {
return new promise(reject => reject(reason))
}
// all
// 所有的传入两个数组,每个都正常返回,即返回resolve,反之reject。
// 同时因为数组中存在触发器执行的情况,他们需要写两个promiseAdd表达式来计算当前promise完成进度。promise.prototype.all = function(values) {
if(!Array.isArray(values)){
return “Arguments should be Array”
}
return new promise((resolve,reject) => {
const arr = []
let index = 0 // 解决多个触发器的并发问题,要采用计数器 function promiseAdd(key,value) {
arr[key] = value
if (++index === values.length) {
resolve(arr)
}
}
for(let i = 0 ;i < values.length;i++) {
let current = values[i]
if (reason instanceof Promise) {
current.then((data) => {
promiseAdd(i,data)
},reject)
} else {
promiseAdd(i,current)
}
}
})
}
// race() 只要有两个实例率先发生改变状况,新的promise的状况就跟着发生改变promise.prototype.race = function race(values) {
if(!Array.isArray(values)){
return “Arguments should be Array”
}
return new promise((resolve, reject) => {
for (let value of values) {
promise.resolve(value).then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}
// 测试标识符
promise.deferred = function () {
let def = {};
def.promise = new promise(function (resolve, reject) {
def.resolve = resolve;
def.reject = reject;
});
return def;
}
module.exports = promise
接下来能采用 Promise test测试是否通过测试
npm install –g promises–aplus–tests
promises–aplus–tests promise.js
全部通过!!!
参考:promise/A+规范化翻译以及手写同时实现 – SegmentFault 思否