手摸手教你实现一个简单的Promise

1,104 阅读4分钟

我们需要Promise吗?

image

看下面这段代码

$.ajax(url1, function(data1){
    // do something...
    $.ajax(url2, function(data2){
        // do something...
        $.ajax(url3, function(data3){
            // do something...
            done(data3); // 返回数据
        })
    });
});
  • 在没有异步对象时候很容易产生callback hell(回调地狱)的问题 层层嵌套的代码极不方便维护 更不方便阅读
  • 其实在Jquery时代就已经有了自己的异步对象$.Deferred比如说我们常用的$.ajax他就是一个异步对象 在使用异步对象修改过后的代码

异步并行请求 并在结果都返回后进行处理

$.when($.ajax({
    url: 'https://www.easy-mock.com/mock/5937664f91470c0ac1073a73/example/test'
}), $.ajax({
    url: 'https://www.easy-mock.com/mock/5937664f91470c0ac1073a73/example/test'
})).done((p1, p2) => {
    console.log(p1, p2)
})
let b = $.Deferred()
function a(dtd) {
    setTimeout(function () {
        console.log(1234)
        dtd.resolve('b is done')
        
    }, 400)
    return dtd

}


$.when(a(b)).done(res => {
    console.log(res)
})

看起来异步对象使用起来还是很方便的它能帮助我们完成一些异步代码采用同步写法就可以完成事情.

Promise是什么?

Promise实现规范 promise A+

  • 是ES6新增的一个特征,它已被列入ES6的正式规范中
  • Promise对象可以理解为一次执行的异步操作,使用promise对象之后可以使用一种链式调用的方式来组织代码;让代码更加的直观。也就是说,有了Promise对象,就可以将异步操作以同步的操作的流程表达出来,避免了层层嵌套的回调函数
  • 解决回调地狱的问题
  • 便于维护
  • 解决同步异步的返回结果 按照顺序
  • 链式调用(JQ 实现链式调用依赖返回this) Promise不能返回this Promise实现链式调用依赖返回一个新的Promise
  • 如果then中无论成功还是失败的回调函数只要有返回值就会走下一个then中的成功 如果有错误就会走下一个then的失败
  • 如果第一个promise 返回了一个普通值,会直接进入下一次then成功的回调,如果第一个promise返回了一个promise需要等待返回的promise执行后的结果传递给下一个then
  • resolvePromise
  • 判断x是否是promise ,如果x是对象并且x的then方法是函数我们就认为他是一个promise
  • promise 值穿透 空then直接穿过
  • Promise 规范中要求,所有onFufiled和onRejected都需要异步执行
graph LR
pendding等待态-->Rejected拒绝态
pendding等待态-->Fulfilled执行态

基本用法

跟$.Deffered很相似 都是依赖返回一个异步对象来解决回调地狱的问题

function test() {
    return new Promise((res, rej) => {
        setTimeout(function () {
            console.log('延迟执行')
            res('done')
        }, 2000)
    })
}
test().then(function (res) {
    console.log(res)
})

实现一个简单的Promise

从上面代码我们可以看出一个Promise有几个特点

  • 参数为一个函数该函数包含两个方法resolve跟reject
  • 返回的实例上包含then方法且then方法的调用时机与resolve和reject的执行时机相关
  • resolve和reject的返回参数直接传入then方法中

初始化一个Promise

function Promise(executor) {
    //executor 执行器
    this.status = 'pending'
    this.reason = null
    this.data = null

    function resolve() {

    }
    function reject() {

    }
    executor(resolve, reject)
}
Promise.prototype.then = function (res, rej) {
    
}


增加对调用resolve 以及reject时数据传入的处理

function Promise(executor) {
    //executor 执行器
    this.status = 'pending'
    this.reason = null
    this.data = null
    const _this = this;
    function resolve(data) {
        if (_this.status == 'pending') {
            _this.data = data
            _this.status = 'onFulfilled'
        }
    }
    function reject(e) {
        if (_this.status == 'pending') {
            _this.reason = e
            _this.status = 'rejected'
        }
    }
    executor(resolve, reject)
}
Promise.prototype.then = function (res, rej) {

}

执行时对Promise状态进行修改以及then函数的调用时机进行处理

function Promise(executor) {
    //executor 执行器
    this.status = 'pending'
    this.reason = null
    this.data = null
    const _this = this;
    function resolve(data) {
        if (_this.status == 'pending') {
            _this.data = data
            _this.status = 'onFulfilled'
        }
    }
    function reject(e) {
        if (_this.status == 'pending') {
            _this.reason = e
            _this.status = 'rejected'
        }
    }
    executor(resolve, reject)
}
Promise.prototype.then = function (res, rej) {
    const _this =  this
    if(_this.status=='onFulfilled'){
        res(_this.data)
        return 
    }
    if(_this.status=='rejected'){
        res(_this.reason)
    }
}

测试走一走

function test() {
    return new Promise(function (resolve, reject) {
        console.log('promise is running')
        resolve('2')
    })
}
test().then((res) => {
    console.log(res)
}, (e) => {
    console.log(e, e)
})

image

OK同步代码通过

接下来实现异步调用

  • 新增两种属性 onFulFilledList onRejectedList 分别在异步执行时调用
function Promise(executor) {
    //executor 执行器
    this.status = 'pending'
    this.reason = null
    this.data = null
    this.onFulFilledList = []
    this.onRejectedList = []
    const _this = this;
    function resolve(data) {
        if (_this.status == 'pending') {
            _this.data = data
            _this.status = 'onFulfilled'
        }
    }
    function reject(e) {
        if (_this.status == 'pending') {
            _this.reason = e
            _this.status = 'rejected'
        }
    }
    executor(resolve, reject)
}
Promise.prototype.then = function (res, rej) {
    const _this = this
    if (_this.status == 'onFulfilled') {
        res(_this.data)
        return
    }
    if (_this.status == 'rejected') {
        rej(_this.reason)
    }
}

对异步调用的成功函数以及失败函数进行队列存储 方便调用 以及调用时直接执行回调队列中方法

function Promise(executor) {
    //executor 执行器
    this.status = 'pending'
    this.reason = null
    this.data = null
    this.onFulFilledList = []
    this.onRejectedList = []
    const _this = this;
    function resolve(data) {
        if (_this.status == 'pending') {
            _this.data = data
            _this.status = 'onFulfilled'
            _this.onFulFilledList.forEach(element => {
                element(_this.data)
            });
        }
    }
    function reject(e) {
        if (_this.status == 'pending') {
            _this.reason = e
            _this.status = 'rejected'
            _this.onRejectedList.forEach(element => {
                element(_this.reason)
            });
        }
    }
    executor(resolve, reject)
}
Promise.prototype.then = function (res, rej) {
    const _this = this
    if (_this.status == 'onFulfilled') {
        res(_this.data)
        return
    }
    if (_this.status == 'rejected') {
        rej(_this.reason)
    }
    if (_this.status == "pending") {
        _this.onFulFilledList.push(res)
        _this.onRejectedList.push(rej)
    }
}

测试代码


function test() {
    return new Promise((res, rej) => {
        setTimeout(function () {
            console.time('promise')
            console.log(123)
            res('4')
        }, 2000)
    })
}
test().then((
    params
) => {
    console.log(params)
    console.timeEnd('a')
})

image

这就实现了最最简单的Promise异步调用了

上面的内容把ES6中的Promise的基本方法给大家讲了一下,希望对大家有多帮助。