说起promise,很多人都不陌生,网络上也有很多手写原理的文章,但是以前在实际运用的时候还是发现有同时对其原理不懂,看规范都是英文,对英文不好的同学来说很费劲,所以专门写了这边文章,第一是给入门的同学提供一些学习资料和手写思路,第二是让自己回顾一下,温故而知新
1:什么是promise
Promise 是ECMAscript 6 原生提供的对象;是异步编程的一种解决方案;
2:基于promise A+规范手写(逐步分析)
1.promise是一个类 在使用的时候 需要new这个类
//所以我们首先定义一个类,不会ES6的同学去先看看class这一块知识
class Promise{
}
2,在newPromise的时候 需要传入一个executor执行器 默认会立即被调用,而且参数有两个 resolve,reject
class Promise{
constructor(exector){
exector(resolve,reject)
}
}
3.promise有三个状态 分别是 pending 默认等待态 fulfilled 成功态 rejected 失败态,我们的promise默认就是pendding 当用户调用resolve时会变成成功态 调用reject的时候会变成失败态,成功可以传入成功的原因 失败可以传入失败的原因
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise{
constructor(exector){
this.status=PENDING;
this.Result=undinfined;
const resolve=(Result)=>{
this.status=FULFILLED;
this.Result=Result;
}
const reject=(Result)=>{
this.status=REJECTED;
this.Result=Result;
}
exector(resolve,reject)
}
}
4.走向失败有两种情况 reject() 用户主抛出异常,还有就是exector函数执行错误,且promise的状态是不能从成功变成失败,也不能从失败变成成功 只有pendingg的时候才能更改状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise{
constructor(exector){
this.status=PENDING;
this.Result=undinfined;
const resolve=(Result)=>{
if(this.status==PENDING){//只有状态是PENDING的时候才可以修改为其他状态
this.status=FULFILLED;
this.Result=Result;
}
}
const reject=(Result)=>{
if(this.status==PENDING){//只有状态是PENDING的时候才可以修改为其他状态
this.status=REJECTED;
this.Result=Result;
}
}
try{
exector(resolve,reject)
}catch(e){
reject(e);
}
}
}
//其实写到这里对promise应该就有个初步的认识了,单这里还没涉及到他的核心方法,如果这一块没弄懂的话不建议往下看了,好好补充一下基础知识,这里涉及到类,回调等知识
5.new Promise 会返回一个promise实例 这个实例上有一个then方法 , then方法中有两个参数一个是成功的回调一个是失败的回调
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise{
constructor(exector){
this.status=PENDING;
this.Result=undinfined;
const resolve=(Result)=>{
if(this.status==PENDING){
this.status=FULFILLED;
this.Result=Result;
}
}
const reject=(Result)=>{
if(this.status==PENDING){
this.status=REJECTED;
this.Result=Result;
}
}
try{
exector(resolve,reject)
}catch(e){
reject(e);
}
}
//then方法,次方法为promise的精髓所在,此事我们先实现单个的then,then链的实现放在后面几步
then(onFulfilled, onRejected){
//如果状态为成功,那就调用成功的方法
if (this.status == FULFILLED) {
onFulfilled(this.value);
}
//如果状态为失败,那就调用失败的方法
if (this.status == REJECTED) {
onRejected(this.reason);
}
}
}
//看到此处你已经逐步接近整个promise的原理了,而且当exector为同步代码的时候已经实现then的调用了,但此时如果你如下操作:
let pro = new Promise((resolve, reject) => {
setTimeout(() => {
reject("ok");
}, 1000);
});
你就会发现问题,你的代码达不到你想要的效果,那么异步的我们怎么处理呢?这就是promise的精髓之一,利用发布订阅原理等到状态改变的时候再去做修改。
6.exector 为异步的处理方式
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise{
constructor(exector){
this.status=PENDING;
this.Result=undinfined;
//至于为什么此处是数组,因为如果你new一个promise实例P1,你P1可以多次点then执行; let p2=p1.then(); let p3=p1.then(); let p4=p1.then(); (发布订阅模式)
this.onResolvedCallbacks = []; // 存放成功的回调
this.onRejectedCallbacks = []; // 存放失败的回调
const resolve=(Result)=>{
if(this.status==PENDING){
this.status=FULFILLED;
this.Result=Result;
//等到成功的时候把存起来的成功的方法执行
this.onResolvedCallbacks.forEach((fn) => fn());
}
}
const reject=(Result)=>{
if(this.status==PENDING){
this.status=REJECTED;
this.Result=Result;
//等到失败的时候把存起来的失败的方法执行
this.onRejectedCallbacks.forEach((fn) => fn());
}
}
try{
exector(resolve,reject)
}catch(e){
reject(e);
}
}
then(onFulfilled, onRejected){
if (this.status == FULFILLED) {
onFulfilled(this.value);
}
if (this.status == REJECTED) {
onRejected(this.reason);
}
if(this.status==PENDING){
//如果exector函数为异步,那么此时执行到then的时候状态还没改变,那么我么就把方法存起来,等到
this.onResolvedCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
}
}
//这一步没弄懂的前提下就别看下面的了,去补习一下发布订阅模式,和JS的同步异步
基本面试的时候能写到这里就差不多了,到时候then链式调用的原理说清楚就可以了,不过有的大公司可能要求你直接写出then 链,那么你继续往下看。
7.then 链的实现
promise 为何能实现链式的写法呢?那是因为每个then方法都返回了一个promise实例,这样promise自然可以调用原型上的方法(此处不懂的就去看看原型原型链的基本知识),废话不多说,上代码:
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise{
constructor(exector){
this.status=PENDING;
this.Result=undinfined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
const resolve=(Result)=>{
if(this.status==PENDING){
this.status=FULFILLED;
this.Result=Result;
this.onResolvedCallbacks.forEach((fn) => fn());
}
}
const reject=(Result)=>{
if(this.status==PENDING){
this.status=REJECTED;
this.Result=Result;
this.onRejectedCallbacks.forEach((fn) => fn());
}
}
try{
exector(resolve,reject)
}catch(e){
reject(e);
}
}
then(onFulfilled, onRejected){
let promise2=new promise((resolve, reject)=>{
if (this.status == FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(x, promise2, resolve, reject);//此处就是更具X的返回值去处理这个promise2的成功和失败的执行
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status == REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if(this.status==PENDING){
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
})
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
})
}
})
return promise2;
}
}
then中的代码解释:
此时我们新增一个promise2,并把它返回,并把上一步处理的方法放倒new 的promise里,因为new promise的时候里面的代码是立即执行的;接下来的东西就有点绕了,仔细看清,这个是promise真正的精髓和重点了:
new的这个promise的是执行resolve,还是reject是取决于你onFulfilled, onRejected的执行结果,(先别纠结定时器,后面回解释),先看里面的 ,此时定义一个X 用来接收onFulfilled, onRejected 的执行结果,在执行中,如果代码直接报错,那么毫不犹豫的走新new的这个promise的reject方法,如果成功就看这个X是什么值,如果这个X是普通值,就把他当成成功,如果X是一个promise对象,则去执行X上的then方法,
为何要用定时器
因为 resolvePromise 中用到了promise2,这个promise2 是在new promise之后产生的,所以要想渠道promise2 必须使用异步,
下面就是resolvePromise 函数
function resolvePromise(x, promise2, resolve, reject) {
// x 决定promise2 的状态 走成功还是失败
if(promise2 === x){
return reject(new TypeError('循环引用'));
}
// 判断x 是不是一个promise 先保证x 得是一个对象或者函数,如果不是对象或者函数那么x 一定不是promise
if((typeof x === 'object' && x !== null) || typeof x === 'function'){
let called;//防止别人的promise调用两次成功或者失败
// 我需要看 这个x 上有没有then方法 有then方法才说明他是一个promise
try{
let then = x.then; // x可能是别人写的promise 那么取then有风险,
if(typeof then === 'function'){
then.call(x,(y)=>{ // x.then((y)=>{},r=>{}) 取then就会有风险
if(called) return;
called = true;
resolvePromise(y,promise2,resolve,reject); // 递归解析直到我们的y的值是一个普通值,因为y可能也是一个promise
},(r)=>{
if(called) return;
called = true;
reject(r);
})
}else{ // 没有then方法的都执行这里
resolve(x); // 只是一个对象而已 就是一个普通值
}
}catch(e){
if(called) return;
called = true;
reject(e);
}
}else{
// x 就是一个普通的值,直接把x 传递给promise2的成功即可
resolve(x);
}
}