PromiseA+规范解读和手promise写部分代码
PromiseA+规范是什么?
Promises/A+为js的promise提供了一个开放的、可靠的、可互操作的JavaScript标准
Promise表示异步操作的最终结果,与promise交互的方式是通过其then方法,该方法注册回调以接收promise的最终值或promise无法实现的原因。
PromiseA+规范官网:https://promisesaplus.com/。
- Promise是ES6中的内置类,是用来解决异步编程【比如数据请求】的,但是兼容性很差
- 从PromiseA+规范来讲,拥有then方法的实例对象[/函数]是promise
不带then链的基础版promise:
(function() {
"use strict"
/*自定义Promise类*/
function Promise(executor) {
let self = this,
change = null;
//Promise函数必须是new执行,【当前对象必须是Promise的实例】
if (!(self instanceof Promise)) throw new TypeError("undefined is not a promise!");
//executor:必须是一个函数
if (typeof executor !== "function") throw new TypeError(`Promise resolver ${executor} is not a function !`);
// 实例具备的属性
self.PromiseState = "pending";
self.PromiseResult = undefined;
self.onFulfilledCallback = []; //成功的回调函数集合
self.onRejectedCallback = []; //失败的回调函数集合
// 修改实例的状态和结果
change = function change(state, result) {
// state :传入的实例的状态
//result :传入的实例的值
//只有状态是pending才可以改变为其他状态
if (self.PromiseState !== "pending") return;
self.PromiseState = state;
self.PromiseResult = result;
// 根据传入的实例的状态,判断需要执行的回调函数集合
var callbacks = state === "fulfilled" ? self.onFulfilledCallback : self.onRejectedCallback,
i = 0,
len = callbacks.length,
callback;
// 创建异步微任务,通知集合中存储的方法执行【前提是集合不为空】
if (callbacks.length > 0) {
setTimeout(function() {
for (; i > len; i++) {
callback = callbacks[i];
if (typeof callback === "function") callback(result);
}
}, 0);
};
};
//new Promise()的时候会立即执行executor函数
try {
executor(function resolve(result) {
change("fulfilled", result);
}, function reject(reason) {
change("rejected", reason);
});
} catch (error) {
// executor函数执行报错也会使状态变成失败
change("rejected", error);
};
// try catch一般情况下是捕捉不到异步的错误的
};
// 构造函数原型
Promise.prototype = {
constructor: "Promise",
wendy: true,
catch: function() {},
finally: function() {},
then: function(onFulfilled, onRejected) {
// queueMicrotask可以创造一个“异步的微任务”,但是兼容性差;可以基于定时器创造一个异步宏任务,来模拟异步微任务
var self = this;
switch (self.PromiseState) {
// 情况1:如果已经知道对应promise的状态是成功或者失败了,则创建一个“异步微任务”,后期执行[onFulfilled/onRejected]
case "fulfilled":
// 用定时器模拟异步微任务
setTimeout(function() { onFulfilled(self.PromiseState) }, 0)
break;
case "fulfilled":
setTimeout(function() { onRejected(self.PromiseState) }, 0);
break;
default:
// 情况2:如果此时还不知道实例状态,就先把[onFulfilled/onRejected]存储起来,后期更改其状态后,再通知方法执行即可,也是异步微任务
self.onFulfilledCallback.push(onFulfilled);
self.onRejectedCallback.push(onRejected);
}
}
};
//支持Symbol再加上类型判断依据属性
if (typeof Symbol !== "undefined") Promise.prototype[Symbol.toStringTag] = "Promise";
//普通对象:私有静态方法
Promise.all = function() {};
Promise.reject = function() {};
Promise.resolve = function() {};
Promise.allSettled = function() {};
Promise.any = function() {};
Promise.race = function() {};
/*暴露API*/
if (typeof window !== "undefined") window.Promise = Promise; // window
if (typeof module === "object" && typeof module.exports === "object") module.exports = Promise; //node
})();
let p1 = new Promise(function executor(resolve, reject) {
resolve("200");
// reject("NO");
});
let p2 = p1.then(result => {
return console.log(result);
}, reason => {
return console.log(reason);
}); // fulfilled
- 插件必须写到闭包里面,保证内部变量和数据不被外界污染
- 自定义一个类并把API(接口)名称暴露出去:
- 暴露给window
- 暴露给一些基于node操作的框架
- Promise必须传入一个函数【executor】,且必须用new执行,否则就报错
- executor函数必须传入两个函数参数resolve, reject
- resolve:把promise状态修改成fulfilled,并把传入的值传递给resolve
- rejected:把promise状态修改成rejected,并把传入的值传递给rejected
- 如果executor函数执行报错,也会触发rejected函数执行,返回值是报错的原因
- promise状态只有是pending是才可以转换成其他状态,一旦被修改,就不再改变。
- new Promise()的时候会立即执行executor函数
- 如果已经知道对应实例的状态是成功或者失败了,则创建一个异步微任务,后期执行[onFulfilled/onRejected]
- 如果此时还不知道实例状态,就先把[onFulfilled/onRejected]存储起来,后期更改其状态后,再通知方法执行即可,也是异步微任务
- 执行change函数:
- 改变promise的状态:判断promise状态是不是pending,如果不是pending,则不再改变
- 判断promise的状态,根据状态的成功或者失败,通知对应的集合[ onFulfilledCallbacks / onRejectedCallbacks ] 中存储的方法执行【创建异步微任务,且前提是集合不为空】
- ES6中的queueMicrotask可以创造一个异步的微任务,但是兼容性差;可以基于定时器创造一个异步宏任务,来模拟异步微任务
- 创建then方法:
- 都会接受两个函数参数 [onFulfilled / onRejected] (方法)
- 并且会返回一个“全新的Promise实例” -> then链
- 执行then方法,首先要判断当前实例状态是成功或者失败:
- 如果已经知道对应实例的状态是成功或者失败了,则创建一个异步微任务,后期执行[onFulfilled/onRejected]
- 如果此时还不知道实例状态,就先把[onFulfilled/onRejected]存储到对应的[ onFulfilledCallbacks / onRejectedCallbacks ] 集合中
相互回调函数处理的模式
【回调函数executor中嵌套[resolve, reject]函数】
- 回调函数:把一个函数executor作为值,传递到另一个执行的函数Promise中,在Promise中可以把executor执行
- 执行0~n 次
- 改变函数中的this
- 还可以给回调函数传递参数(例如:[resolve, reject]函数)
- 接收函数的返回值
- ……
THEN链机制处理
- 每一次执行then都会返回一个“全新的promise实例【@TP】”【THEN链】
- 实例状态是由上个then中传入的[onFulfilled/onRejected]任一方法执行结果决定的,看方法执行后的返回值 :
- 如果返回的不是Promise实例:
- 方法执行不报错,新实例的状态就是fulfilled,并且return的结果是新实例的结果
- 方法执行报错,新实例就是失败的,结果是失败的原因【新实例的结果就是失败的】
- 如果返回的也是一个Promise实例【@RP】:
- 则【@RP】的状态和结果直接影响了【@TP】的状态和结果。
- 如果返回的不是Promise实例:
- promise的穿透【顺延】
/*自定义Promise类*/
(function(){
function Promise(executor) {/*...*/};
// 校验是否为Promise实例
function isPromise(x) {
if (x == null) return false; //传入的x是null或者undefined则返回false
if (/^(object|function)$/i.test(typeof x)) {
// promise必须是一个函数或者对象
if (typeof x.then === "function") return true;
// promise必须有then方法
};
return false;// x 不是对象或者函数的返回false
};
// 处理onFulfilled或者onRejected方法执行的返回结果的处理
function handle(newPromise, x, resolve, reject) {
//x不能是新创建的promise实例
if (x === newPromise) throw new TypeError("Chaining cycle detected for promise #<Promise>");
//x必须是一个promise实例
if (isPromise(x)) {
try {
x.then(resolve, reject);
} catch (error) {
reject(error);
};
return;
};
// 如果返回的不是promise实例,且没有报错,则promise实例是成功的,x是结果
resolve(x);
}
Promise.prototype = {
//...,此处只真是then函数相关代码,其余代码省略
then: function(onFulfilled, onRejected) {
var self = this,
newPromise, //创建一个新的promise实例,并返回该promise实例。
x; //方法执行的返回结果
//onFulfilled不传值的情况下默认返回一个成功的promise
if (typeof onFulfilled !== "function") {
onFulfilled = function onFulfilled(result) {
return result;
};
};
//onRejected不传值的情况下默认返回一个失败的promise
if (typeof onRejected !== "function") {
onRejected = function onRejected(reason) {
throw reason;
};
};
//resolve & reject 可以设置返回的@TP(新promise实例)是成功还是失败以及结果等
// 但是执行哪个方法,由要监听onFulfilled|onRejected方法报错和返回值来决定。
newPromise = new Promise(function(resolve, reject) {
//执行不报错则根据返回的结果来处理返回的promise实例
// 执行报错执行reject,返回的是报错信息。
switch (self.PromiseState) {
case "fulfilled":
setTimeout(function() {
try {
x = onFulfilled(self.PromiseResult);
handle(newPromise, x, resolve, reject);
} catch (error) {
reject(error);
};
}, 0)
break;
case "rejected":
setTimeout(function() {
try {
x = onRejected(self.PromiseResult);
handle(newPromise, x, resolve, reject);
} catch (error) {
reject(error);
};
}, 0);
break;
default:
//类似bind机制,
//为了保证方法执行时,方法的上级上下文是当前上下【这样才能获取到当前上下文中的变量】,把需要操作的事情放到一个匿名函数中,把匿名函数传递给onFulfilledCallbacks | onRejectedCallbacks。
//依然需要处理执行过程中的报错行为。
self.onFulfilledCallbacks.push(function(result) {
try {
x = onFulfilled(result);
handle(newPromise, x, resolve, reject);
} catch (error) {
reject(error);
}
});
self.onRejectedCallbacks.push(function(reason) {
try {
x = onRejected(reason);
handle(newPromise, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
});
return newPromise;//返回新创建的promise
};
};
//...
})();
let p1 = new Promise(function executor(resolve, reject) {
resolve("200");
// reject("NO");
});
let p2 = p1.then(result => {
console.log("成功->", result);
return 10;
}, reason => {
console.log("失败->", reason);
return 10;
});
let p3 = p2.then(result => {
console.log("成功->", result);
throw "XXX";
}, reason => {
console.log("失败->", reason);
});
let p4 = p3.then(result => {
console.log("成功->", result);
}, reason => {
console.log("失败->", reason);
});
// 成功-> 200
// 成功-> 10
// 失败-> XXX
Promise.resolve
Promise.resolve()返回一个成功的promise实例,返回值是传入的值
Promise.resolve = function(result) {
return new Promise(function(resolve){
resolve(result)
});
};
Promise.reject & Promise.prototype.catch
Promise.reject(reason):返回一个失败的promise,返回值是传入的值
Promise.prototype.catch(onRejected):方法返回一个Promise,并且处理拒绝的情况
Promise.reject = function(reason) {
// _,占位,第一个函数不需要传的时候
return new Promise(function(_, reject){
reject(reason);
});
};
Promise.prototype={
catch:function(onRejected) {
return this.then(null, onRejected);
};
}
Promise.all
- 语法:Promise.all(iterable);
- iterable: 一个可迭代对象,如 Array 或 String
- 返回值:只返回一个Promise实例
- 完成(Fulfillment):结果是按照顺序依次存储每一个promise实例的返回值
- 当且仅当Promise.all传入的可迭代对象为空时,会同步地返回一个已完成[resolved]状态的promise
- 传入的 promise 都变为完成状态时,或者传入的可迭代对象内没有 promise,则返回的 promise 异步地变为完成
- 若参数包含非 promise 值,则忽略,在promise完成后将其放在返回数组中
- 在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组,它包含所有的传入迭代参数对象的值(也包括非 promise 值)
- 失败/拒绝(Rejection):
- 如果传入的 promise 中有一个失败(rejected),Promise.all 异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise 是否完成。
- 完成(Fulfillment):结果是按照顺序依次存储每一个promise实例的返回值
Promise.all = function(promises) {
let newPromise = null, //返回的全新的promise实例
results = [], //成功的promise实例的但返回值的数组
n = 0; //计数器
//传入的必须是一个数组
if (!(Array.isArray(promises))) throw new TypeError(promises, "is not iterable!");
//控制集合中的每一项都是promise实例,
promises = promises.map(function(promise) {
//不是promise的项变成一个成功的promise实例
if (!isPromise(promise)) return Promise.resolve(promise);
return promise;
});
// 创建一个全新的promise实例
newPromise = new Promise(function(resolve, reject) {
promises.forEach(function(promiseItem, index) {
promiseItem.then(function(result) {
//存储的是当前这一项成功的结果,并且保持顺序不变
results[index] = result;
//每处理一次,n+1
++n;
// 全部处理成功了,返回成功的promise实例
if (n >= promises.length) resolve(results);
}).catch(function(reason) {
reject(reason); //只要有一项失败,则promise.all返回失败
})
});
});
return newPromise;// 返回这个全新的promise实例
};
// ...
let p1 = Promise.resolve(10);
let p2 = new Promise(resolve => {
setTimeout(() => {
resolve(20)
}, 1000)
});
let p3 = 30;
Promise.all([p1, p2, p3]).then(results => {
console.log("success", results);
}).catch(reason => {
console.log("fail", reason);
});//success (3) [10, 20, 30]
Promise.prototype.finally()
- 语法:
p.finally(onFinally);
- 参数:
- onFinally:Promise 结束后调用的Function。
- onFinally()函数执行没有参数,并且会把结果和报错信息传递下去
- 在finally回调中 throw(或返回被拒绝的promise)将以 throw() 指定的原因拒绝新的promise.
- 返回值:返回一个设置了 finally 回调函数的Promise对象。
- 在 promise 结束时,无论 Promise 运行成功还是失败,都会运行 finally
Promise.prototype = {
finally: function(onFinally) {
return this.then(function onFulfilled(resolve) {
return Promise.resolve(onFinally()).then(function() {
return resolve;
})
}, function onRejected(reason) {
return Promise.resolve(onFinally()).then(function() {
throw reason;
});
})
},
}