zoukankan      html  css  js  c++  java
  • 简单实现Promise

    简单实现Promise

    2019年08月18日 17:24:12 yhy1315 阅读数 3 标签: Promise实现JavaScript 更多

    个人分类: JavaScript

    版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

    本文链接:https://blog.csdn.net/yhy1315/article/details/99707014

    简单实现Promise

    Promise在我们平时写js中经常用到,(如果你没有经常用到,也许你该好好学习一下ES6了!)但只有知道其内部实现原理才能更好的使用它,所以我们今天来一起深入学习Promise!

    了解Promise

    学习一个东西,最好是从它的历史学起,为什么它会出现?为什么它更好?为什么它这样设计?保留这些问题,我们将一一解释。

    不够优雅的callback

    基于单线程js的开发都快把人搞秃了,想象一种情形:用户点击提交你页面表单,为了让用户有更好的体验,我们不跳转式提交表单,那么我们就要在JavaScript线程中提交。但是我们很穷,服务器的带宽只有1Mbps,这会导致什么?JavaScript在提交表单后苦苦等待服务器响应,但是由于我们网速太慢,无法及时获得相应,用户也不耐烦在页面瞎点(永远不要信任用户!),用户会发现所有页面相应都无法获得(主线程在处理相应!),那他就会放弃这家网站!

    没错,单线程就会出现这些事情,后来聪明的工程师想出了一个办法,通过事件机制来回调异步操作!那么,上面情况就轻而易举的解决了!我们只需要监听ajax的onMessage方法,并在方法中调用我们的callback函数即可!

    回调虽然很好,但是有一个比较严重的问题!看下面例子。

    ajax.onMessage(function(msg){

    let ajax2 = Ajax.get("www.domain/controller.com")

    ajax2.onMessage(function(msg){

    let ajax3 = Ajax.get(".....")

    ajax3.onMessage(function(msg){

    //....

    })

    })})

    哦!回调越来越多,我们好像无法掌握这串代码了!这就是回调不足之处!回调地狱。

    拯救世界的Promise

    那么有没有一种方法,能让我们想同步一样解决异步问题呢?聪明的工程师又想到一种方法,我们应该将这些回调封装成一个函数,供我们随时使用,而且为了解决嵌套的可读性差,我们应该使用链式调用(相信我,链式真的比嵌套好)!

    来看一下Promise的使用:

    //Ajax全为抽象ajax操作,不要将它带入任何实际操作!//我们封装了一个ajax在Promise中的操作let ajaxToPromise = url=>{

    return new Promise((resolve,reject)=>{

    //为了方便在本地测试,我们不用ajax请求,我们改用setTimeOut方法,相信聪明的你一定能理解

    setTimeout(()=>{

    resolve()

    console.log("延迟 " + url + " ms的操作被完成!")

    },url);

    })}//封装完成 我们看看使用时能为我们提供什么便利let url1,url2,url3;ajaxToPromise(1000).then(()=>{return ajaxToPromise(1000)}).then(()=>{return ajaxToPromise(1000)})

    如果你运行,你会发现每隔1000ms就会有输出。

    很清楚,我们通过我们封装的ajaxToPromise方法,一行解决了异步请求多次问题!接下来,我们将会知道Promise究竟在做什么?

    new Promise( function(resolve, reject) {...} /* executor */ );

    简单理解下:Promise构造函数仅仅接受函数作为参数,并在构造方法中立即调用该函数。以后Promise会作为该函数的代理,在该函数改变Promise状态值时,可以使用then()方法执行后续操作。

    Promise的内部实现

    如果你只对Promise的运行与使用感兴趣你可以跳过这部分,但是我会推荐任何人读它。

    Promise内部使用回调函数调用then()方法注册的回调,在什么时候用?我们显式调用resolve()就会更改Promise状态,相应调用在then()中注册的回调。

    如果你看不太懂,也没有关系,毕竟语言是很抽象的,我们直接上代码,不用担心,我们不会直接写出Promise全部代码,一点一点来才利于学习。

    构造函数实现

    希望大家都喜欢ES6的语法!(笑

    class MyPromise{

    /**

    * 构造函数 立即执行callback函数

    * @param {Function} callback

    */

    constructor(callback) {

    if(this.isFunction(callback)){

    callback()

    }else{

    throw new Error("MyPromise 必须接受一个函数参数!")

    }

    }

    /**

    * 判断func是否为Function对象

    * @param {Function} func

    */

    isFunction(func){

    return typeof func == "function"

    }}

    构造方法已经完成,但是我们注意力应该放在then()方法和resolve()方法。下面我们先考虑then()方法的实现。

    then()方法实现

    then()方法应该去注册执行回调函数,而本身不执行,选择让用户在callback方法中执行是更好的选择。那么我们注册的方法应该存放一下,选择队列存放吧!我们维护两个队列onFulfilledQueue与onRejectedQueue,我们创建一个方法initData(),其中初始化我们所有的成员变量。

    class MyPromise{

    constructor(callback) {

    if(this.isFunction(callback)){

    this.initData()//初始化变量

    callback()

    }else{

    throw new Error("MyPromise 必须接受一个函数参数!")

    }

    }

    //...上面代码这里省略

    /**

    * 初始化MyPromise所谓成员变量

    */

    initData(){

    this.callback = null;//用户传入构造的callback

    this.onFulfilledQueue = []//注册的成功回调

    this.onRejectedQueue = []//注册的失败回调

    this.value = null;//要返回的成功值

    this.error = null;//要返回的失败值

    }}

    then()方法,接受最多两个参数onFulfilled,onRejected分别应当在状态是成功与失败时调用,那么我们先引入状态!

    // 定义Promise的三种状态常量// 把下面代码加入你的initData()中this.PENDING = 0//进行中this.FULFILLED = 1//已完成this.REJECTED = -1//以失败this.status = this.PENDING//现在状态 默认为进行中

    ok,到这一步,我们封装了成员变量初始化,为我们的Mypromise引入了状态机制,then()的雏形可以写出来了。

    /**

    * then方法最多接受两个函数类型参数

    * 如果此时MyPromise状态为完成将会执行onFulfilled

    * 若为错误执行onRejected

    * @param {Function} onFulfilled

    * @param {Function} onRejected

    */

    then(onFulfilled, onRejected) {

    if(this.status == this.PENDING){//未完成时 加入队列

    if(this.isFunction(onFulfilled))

    this.onFulfilledQueue.push(onFulfilled)

    if(this.isFunction(onRejected))

    this.onRejectedQueue.push(onRejected)

    }else if(this.status == this.FULFILLED&& this.isFunction(onFulfilled)){

    onFulfilled(this.value)//如果这时候状态改变为已完成 我们直接调用onFulfilled

    }else if(this.status==this.REJECTED&&this.isFunction(onRejected)){

    onRejected(this.error)//这里同上面

    }

    }

    基本雏形出来了,但是我们还要考虑,因为then()要支持链式调用,所以我们应该在最后返回MyPromise的一个实例。

    但是又出现了新的问题,我们返回的MyPromise实例什么时候改变状态?我们应该依赖于上个MyPromise的then()方法的返回值,所以我们要大改代码

    继续完善代码

    then(onFulfilled, onRejected) {

    return new MyPromise((onFulfilledNext, onRejectedNext) => {

    let fulfilled = value => {

    try {

    if (!this.isFunction(onFulfilled)) {

    onFulfilledNext(value)

    } else {

    let res = onFulfilled(value);

    if (res instanceof MyPromise) {

    res.then(onFulfilledNext, onRejectedNext);

    } else {

    onFulfilledNext(res)

    }

    }

    } catch (error) {

    onRejectedNext(error)

    }

    }

    let rejected = error => {

    try {

    if (!this.isFunction(onRejected)) {

    onRejectedNext(error)

    } else {

    let res = onRejected(error);

    if (res instanceof MyPromise) {

    // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调

    res.then(onFulfilledNext, onRejectedNext)

    } else {

    //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数

    onFulfilledNext(res)

    }

    }

    } catch (err) {

    // 如果函数执行出错,新的Promise对象的状态为失败

    onRejectedNext(err)

    }

    }

    switch (this.status) {

    // 当状态为pending时,将then方法回调函数假如执行队列等待执行

    case this.PENDING:

    this.onFulfilledQueue.push(fulfilled)

    this.onRejectedQueue.push(rejected)

    break

    // 当状态已经改变时,立即执行对应的回调函数

    case this.FULFILLED:

    fulfilled(this.value)

    break

    case this.REJECTED:

    rejected(this.error)

    break

    }

    });

    }

    现在代码很完美,我们then中返回的MyPromise依赖于上个then的调用及返回值了。

    实现_resolve与_reject方法

    这一节就比较简单,我们要干的事情,就是判断状态,并全部调用队列中的函数。

    _resolve(value) {

    if (this.status != this.PENDING) return;

    this.status = this.FULFILLED;

    this.value = value;

    const run = () => {

    while (this.onFulfilledQueue.length != 0) {

    let cb = this.onFulfilledQueue.shift()

    cb(this.value)

    }

    }

    setTimeout(run, 0//要在MyPromise中支持同步操作 也就是resolve()之后操作先于then()中操作

    //不太懂的同学可以看看我的有一篇详细讲解js中的event loop

    }

    _reject(error) {

    if (this.status != this.PENDING) return

    this.status = this.REJECTED

    this.error = error

    const run = () => {

    while (this.onRejectedQueue.length != 0) {

    let cb = this.onRejectedQueue.shift()

    cb(this.error)

    }

    }

    setTimeout(run, 0)

    }

    catch实现

    catch(onRejected){

    console.log(onRejected)

    return this.then(null,onRejected)

    }

    catch()方法本身只是then()方法的一种调用。

    结尾

    脑瘫码农 纯靠自学 如有错误 望请指正 共同学习 不胜感激

    参考

    Promise-MDN

    一步步实现Promise代码封装

    Promise实现原理(附源码)

  • 相关阅读:
    d3.js(v5.7)的比例尺以及坐标轴
    d3.js(v5.7)的node与数据匹配(自动匹配扩展函数)
    d3.js(v5.7)的attr()函数完善(添加obj支持)
    d3.js入门之DOM操作
    d3.js入门学习
    最近在写个人网站,忙碌中。。。
    构建vue项目(vue 2.x)时的一些配置问题(持续更新)
    Python之元组
    Python之列表
    Python之字符串
  • 原文地址:https://www.cnblogs.com/grj001/p/12224359.html
Copyright © 2011-2022 走看看