VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > temp > JavaScript教程 >
  • 基于promise a+规范手写promise(promise其实没那么难!)

一、promise解决了哪些问题?

  1. 异步并发 待所有异步任务结束之后再执行我们的业务逻辑。
  2. 回调嵌套

二、promise特点

1. 每个promise都有三个状态,pending等待状态 fulfilled成功态 rejected失败态
2. 状态不可逆(若状态变成了成功态,则会一直维持这个状态)
3. 每个promise都有一个then方法 传入两个参数 一个是成功回调(参数是resolve执行时传的值) 一个是失败回调(参数是reject执行时传的值)
4. new Promise是同步执行的
5. 抛出异常的话 也会变成失败状态 走失败的回调
 

三、简单示例

复制代码
    new Promise((resolve,reject) => {
      // 1.状态从pending改为fulfilled成功态,执行成功的回调
      resolve('success')
      // 2.状态从pending改为rejected失败态,执行失败的回调
      // reject('fail')
      // throw new Error('fail')
    }).then((data) => {
      console.log('success', data);
    }, (err) => {
      console.log('fail', err);
    })
复制代码

四、手写实现

0.0.1版

复制代码
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
  constructor(executor) {
    // 初始状态为pending等待状态
    this.status = PENDING;
    const resolve = (value) => {
      // 执行resolve状态变为成功态
      this.status = FULFILLED;
    };
    const reject = (reason) => {
      // 执行reject状态变为失败态
      this.status = REJECTED;
    };
    try {
      // 执行传入的回调函数
      executor(resolve, reject);
    } catch (e) {
      // 回调函数执行出错,也会执行reject
      reject(e);
    }
  }
}
复制代码

由于promise的状态一旦转成了成功态或者失败态,就不能再改变状态了,所以我们需要加个判断,只有当状态为pending等待态的时候,才能将状态改为成功或失败,所以当状态变为了成功,再去调用resolve的话,无法满足判断条件,就不会继续执行了。

0.0.2版

复制代码
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
  constructor(executor) {
    // 初始状态为pending等待状态
    this.status = PENDING;
    const resolve = (value) => {
      // 执行resolve状态变为成功态
      if (this.status === PENDING) {
        this.status = FULFILLED;
      }
    };
    const reject = (reason) => {
      // 执行reject状态变为失败态
      if (this.status === PENDING) {
        this.status = REJECTED;
      }
    };
    try {
      // 执行传入的回调函数
      executor(resolve, reject);
    } catch (e) {
      // 回调函数执行出错,也会执行reject
      reject(e);
    }
  }
}
复制代码

这样调用的话,状态改变之后就不会再改了

    let p = new MyPromise((resolve,reject) => {
      resolve('success')
      reject('success')
    })
    console.log('p: ', p);

 

接下来我们实现一下promise实例的then方法,then方法的特点在上面有提到哦

0.0.3版

复制代码
class MyPromise {
  constructor(executor) {
    // 赋值到this上是为了方便在then方法中调用
    // 初始状态为pending等待状态
    this.status = PENDING;
    // 成功回调的参数
    this.value = undefined;
    // 失败回调的参数
    this.reason = undefined;
    const resolve = (value) => {
      // 执行resolve状态变为成功态 保存用户传入的参数
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
      }
    };
    const reject = (reason) => {
      // 执行reject状态变为失败态 保存用户传入的参数
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
      }
    };
    try {
      // 执行传入的回调函数
      executor(resolve, reject);
    } catch (e) {
      // 回调函数执行出错,也会执行reject
      reject(e);
    }
  }
  // 接收两个函数作为参数,参数是用户传的,传的第一个回调就代表成功的回调,传的第二个回调就代表失败的回调
  then(onFulfilled, onRejected) {
    // 根据当前的状态,执行对应的回调。回调的参数为用户调用resolve或者reject传入的数据
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }
    if (this.status === REJECTED) {
      onRejected(this.reason);
    }
  }
}
复制代码

调用时

复制代码
    let p = new MyPromise((resolve,reject) => {
      resolve('success')
    })
    p.then((value) => {
      console.log(value);// success
    }, (reason) => {
      console.log(reason);
    })
复制代码

 但是如果是在异步代码里面调用resolve方法,就不会执行成功回调了,例如:

复制代码
    let p = new MyPromise((resolve,reject) => {
      setTimeout(() => {
        resolve('success')
      }, 300);
    })
    p.then((value) => {
      console.log(value);// success
    }, (reason) => {
      console.log(reason);
    })
复制代码

这是因为代码执行到settimeout,会将回调放入宏任务队列,而不是立即执行,所以会先执行then方法,这个时候settimeout并未执行,所以resolve也未执行,此时的status = 'pending',then方法的两个回调都不会执行。

因此,我们想要执行异步代码里的resolve或者reject函数的话,需要在执行then方法的时候,先把成功回调或失败回调先存起来,直到异步代码执行到resolve,再依次执行回调。实现方式就是采用发布订阅模式,借助两个数组,一个是成功回调的数组,一个是失败回调的数组,在调用then时,若status = pending,就把回调存入对应的数组,之后在resolve(reject)函数中依次调用成功(失败)回调的数组的回调。

0.0.4版

复制代码
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
  constructor(executor) {
    // 初始状态为pending等待状态
    this.status = PENDING;
    // 成功回调的参数
    this.value = undefined;
    // 失败回调的参数
    this.reason = undefined;
    this.onFulFilledCallbacks = []; // 存放成功的回调
    this.onRejectedCallbacks = []; // 存放失败的回调
    const resolve = (value) => {
      // 执行resolve状态变为成功态 保存用户传入的参数
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onFulFilledCallbacks.forEach((fn) => fn()); // 依次执行成功回调队列的回调
      }
    };
    const reject = (reason) => {
      // 执行reject状态变为失败态 保存用户传入的参数
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) => fn()); // 依次执行失败回调队列的回调
      }
    };
    try {
      // 执行传入的回调函数
      executor(resolve, reject);
    } catch (e) {
      // 回调函数执行出错,也会执行reject
      reject(e);
    }
  }
  // 接收两个函数作为参数,参数是用户传的,传的第一个回调就代表成功的回调,传的第二个回调就代表失败的回调
  then(onFulfilled, onRejected) {
    // 根据当前的状态,执行对应的回调。回调的参数为用户调用resolve或者reject传入的数据
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }
    if (this.status === REJECTED) {
      onRejected(this.reason);
    }
    // 状态为pending时 将用户传的回调存放到各自的队列中(若用户没有调用resolve或reject,则不会执行队列中的回调)
    if (this.status === PENDING) {
      this.onFulFilledCallbacks.push(() => {
        onFulfilled(this.value);
      });
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason);
      });
    }
  }
}
复制代码

调用时

复制代码
    let p = new MyPromise((resolve,reject) => {
      setTimeout(() => {
        resolve('success')
      }, 300);
    })
    p.then((value) => {
      console.log(value, 1);// success 1
    }, (reason) => {
      console.log(reason);
    })
    p.then((value) => {
      console.log(value, 2);// success 2
    }, (reason) => {
      console.log(reason);
    })
复制代码

接下来我们来分析then链式调用的规则

(1)如果then方法中成功回调或失败回调返回的是一个非promise值,则将这个值传递给外层下一次then的成功回调参数

(2)如果then方法中成功回调或失败回调的执行报错了,则将错误信息传递给外层下一次then的失败回调参数

复制代码
    let pp = new Promise((resolve,reject) => {
      resolve(1)
    }).then((data) => {
      console.log('第一次 success', data);//第一次 success 1
      return 100;
      // throw new Error('error')
    }, (err) => {
      console.log('第一次 fail', err);
    }).then((data) => {
      console.log('第二次 success', data);//第二次 success 100
    }, (err) => {
      console.log('第二次 fail', err);
    })
复制代码

(3)如果then方法中成功回调返回的是一个promise值,

  • 若在返回的这个promise内部调用了resolve函数,则将传入resolve的参数 传递给外层下一次then的成功回调参数;
  • 若在返回的这个promise内部调用了reject函数,则将传入reject的参数,传递给外层下一次then的失败回调参数;
复制代码
    let pp2 = new Promise((resolve,reject) => {
      resolve(1)
    }).then((data) => {
      console.log('第一次 success', data);//第一次 success 1
      return new Promise((resolve, reject) => {
        resolve(100);
        // reject(200)
      });
    }, (err) => {
      console.log('第一次 fail', err);
    }).then((data) => {
      console.log('第二次 success', data);//第二次 success 100
    }, (err) => {
      console.log('第二次 fail', err);
    })
复制代码

(4)catch方法相当于then(null, err => {})

复制代码
    let pp2 = new Promise((resolve,reject) => {
      resolve(1)
    }).then((data) => {
      console.log('第一次 success', data);//第一次 success 1
      return new Promise((resolve, reject) => {
        // resolve(100);
        reject(200);
      });
    }, (err) => {
      console.log('第一次 fail', err);
    }).catch((data) => {
      console.log('第二次 fail', data);//第二次 fail 200
    });
复制代码

then的链式调用是如何实现的?

每次调用then,返回一个新的promise实例,这个实例上肯定也有then方法,就可以一直.then下去

0.0.5版

复制代码
class MyPromise {
  constructor(executor) {
    // 省略了跟上一版一样的内容
  }
  then(onFulfilled, onRejected) {
    // 调用then的时候 会创建一个新的promise实例并返回
    let promise2 = new MyPromise((resolve, reject) => {
      // 这里面的resolve和reject是promise2的 当在promise2里面调用resolve,就会执行promise2.then里面的成功回调
      if (this.status === FULFILLED) {
        // 需要拿到成功回调的返回值,传递给下一个then
        let x = onFulfilled(this.value);
        resolve(x);
      }
      if (this.status === REJECTED) {
        let x = onRejected(this.reason);
        resolve(x);
      }
      if (this.status === PENDING) {
        this.onFulFilledCallbacks.push(() => {
          let x = onFulfilled(this.value);
          resolve(x);
        });
        this.onRejectedCallbacks.push(() => {
          let x = onRejected(this.reason);
          resolve(x);
        });
      }
    });
    return promise2;
  }
}
复制代码

这里涉及到MyPromise函数的递归执行,在调用函数时进行拆分,就好分析了

复制代码
    let pp = new Promise((resolve,reject) => {
      // resolve(1)
      reject(1)
    })
    // 调用pp的then会返回promise2
    let promise2 = pp.then(data => {
      // 执行pp的成功回调 返回普通值x 会触发promise2的成功回调 相当于在这个promise2的内部调用resolve(x)
      // return 100;
    }, err => {
      // 执行pp的失败回调 也会触发promise2的成功回调
      return 100;
    })
    promise2.then((data) => {
      console.log(data);// 100
    }, (err) => {
      console.log(err);
    })
复制代码

 上个版本只是实现了规则(1),处理的是成功或失败回调返回非promise值的情况,下面处理一下成功或失败的回调在执行时发生报错的情况,只需要加上try catch即可

0.0.6版

复制代码
class MyPromise {
  constructor(executor) {
    // 此处省略
  }
  then(onFulfilled, onRejected) {
    // 调用then的时候 会创建一个新的promise实例并返回
    let promise2 = new MyPromise((resolve, reject) => {
      // 这里面的resolve和reject是promise2的 当在promise2里面调用resolve,就会执行promise2.then里面的成功回调
      if (this.status === FULFILLED) {
        // 需要拿到成功回调的返回值,传递给下一个then
        try {
          let x = onFulfilled(this.value);
          resolve(x);
        } catch (e) {
          reject(e);
        }
      }
      if (this.status === REJECTED) {
        try {
          let x = onRejected(this.reason);
          resolve(x);
        } catch (e) {
          reject(e);
        }
      }
      if (this.status === PENDING) {
        this.onFulFilledCallbacks.push(() => {
          try {
            let x = onFulfilled(this.value);
            resolve(x);
          } catch (e) {
            reject(e);
          }
        });
        this.onRejectedCallbacks.push(() => {
          try {
            let x = onRejected(this.reason);
            resolve(x);
          } catch (e) {
            reject(e);
          }
        });
      }
    });
    return promise2;
  }
}
复制代码

接下来处理规则(3),也就是then方法中返回一个promise值的情况,需要给返回值注册成功和失败的回调,成功回调中执行promise2的resolve,失败回调中执行promise2的reject

规则(4)实际就是调用了then方法

0.0.7版(最终版)

复制代码
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function resolvePromise(x, promise2, resolve, reject) {
  // then中成功或失败回调的返回值 不能和调用then方法返回的promise值相等
  if (x === promise2) {
    return reject(new TypeError('出错'));
  }
  if (x instanceof MyPromise) {
    try {
      let then = x.then; 
      then.call(
        x,
        (y) => {
          // y代表返回的promise值内部调用resolve时传的参数 传给promise2的resolve方法并执行
          resolve(y);
        },
        (r) => {
          // r代表返回的promise值内部调用reject时传的参数 传给promise2的reject方法并执行
          reject(r);
        },
      );
    } catch (e) {
      reject(e);
    }
  } else {
    // 非promise值
    resolve(x);
  }
}
class MyPromise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onFulFilledCallbacks = []; // 存放成功的回调
    this.onRejectedCallbacks = []; // 存放失败的回调
    const resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onFulFilledCallbacks.forEach((fn) => fn()); // 依次执行成功回调队列的回调
      }
    };
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) => fn()); // 依次执行失败回调队列的回调
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
  then(onFulfilled, onRejected) {
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // 使用settimeout是为了异步执行处理返回值的代码 否则直接使用promise2会报错
        setTimeout(() => {
          // 需要拿到成功回调的返回值,传递给下一个then
          try {
            let x = onFulfilled(this.value);
            // 统一处理返回值
            resolvePromise(x, promise2, resolve, reject);
          } catch (e) {
            console.log(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.onFulFilledCallbacks.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;
  }
    catch(onRejected) {
      return this.then(null, onRejected);
    }
}
复制代码

 调用时

复制代码
    let pp = new MyPromise((resolve,reject) => {
      resolve(1)
    })
    // 调用pp的then会返回promise2
    let promise2 = pp.then(data => {
      // 执行pp的成功回调 返回promise值x 会给x注册成功和失败回调,
      // 成功回调中执行promise2的成功回调,失败回调中执行promise2的失败回调
      return new MyPromise((res, rej) => {
        res(3000)
      })
    })
    // 若执行promise类型的返回值的resolve,就会执行第一个回调
    promise2.then((data) => {
      console.log(data);// 3000
    }, (err) => {
      console.log(err);
    })
复制代码

 

 好啦!终于整理完成了!看完之后是不是发现promise其实也没那么难了!

来源:https://www.cnblogs.com/zhengrongbaba/p/15161150.html



相关教程