js的Promise

异步任务

js中,有些任务并不是立即执行的,如setTimeOut或者发送网络请求等等
这些任务或多或少的延时,而这些延时都可能会导致js代码实际的执行顺序和我们预计的不一样
有些时候我们也会需要这些任务完成后的返回值来进行进一步的操作
如下面代码所示

function ajax(url) {
    setTimeout(function () {
        if (url == "http://yes") {
            return true
        } else {
            return false
        }
    }, 1000)
}
var data = ajax("http://yes")
 console.log(data)

这段代码中我们模拟了一个网络请求,希望data能接受到服务器返回的结果,可打印的结果却是undefined
结果

回调地狱

为了保证代码的执行顺序,也为了保证我们能拿到需要的返回值,解决以上问题的方法就是使用回调函数
我们将以上的代码改写一下

function ajax(url, success, fail) {
    setTimeout(function () {
        if (url == "http://yes") {
            success("请求成功")
        } else {
            fail("请求失败")
        }
    }, 1000)
}
ajax("http://yes", function (res) {
            console.log(res)
        }, function (err) {
            console.log(err)
})

结果

结果
我们会在1秒之后拿到结果

现在,我们的需求变成了需要监听页面中的一个按钮,当用户点击了这个按钮的1秒后发送网络请求
代码就变成了下面这样

function ajax(url, success, fail) {
    setTimeout(function () {
        if (url == "http://yes") {
            success("请求成功")
        } else {
            fail("请求失败")
        }
    }, 1000)
}
document.querySelector("button").addEventListener("click", function () {
    setTimeout(function () {
        ajax("http://yes", function (res) {
            console.log(res)
        }, function (err) {
            console.log(err)
        })
    }, 1000)
})

这种回调函数嵌套回调函数的做法被称之为回调地域,为了能保证任务的执行顺序,以上代码都是通过硬编码的形式解决
我们将setTimeOut硬编码到click中,再将ajax硬编码到setTimeOut中,以此类推
这种硬编码所带来的无法复用以及难以处理每个步骤中发生的异常情况的问题是回调地狱真正问题所在

Promise

ES6之后规定了一种新的编程范式Promise,它可以很轻松的解决以上问题
通过new创建Promise对象时,我们需要传入一个回调函数,这个回调函数被称之为executor
这个回调函数会立即执行,并且在传入2个回调函数resolvereject
当调用resolve方法时会调用Promise对象的then方法传入的回调函数
当调用reject方法时就会调用Promise对象的catch方法传入的回调函数

Promise的三种状态

var promise = new Promise((resolve, reject) => {
    resolve("success")
    reject("fail")
})
promise.then((res) => {
    console.log(res)
}).catch((err) => {
    console.log(err)
}) 

Promise的使用过程中会出现三种状态

  1. fulfilled
    也叫做兑现状态,位于这种状态时表示Promise中的操作成功完成,即调用了resolve方法
  2. rejected
    也被叫做拒绝状态,位于这种状态时表示Promise中的操作失败,即调用了reject方法
  3. pending
    也被叫做初始状态,这种状态意味着此时Promise既没有兑现也没有拒绝,当Promise没有调用resolvereject方法时Promise位于这种状态
    值得注意的是Promise可以从pending状态变成fulfilledrejected状态
    Promise无法从fulfilled/rejected变为pendingrejected/fulfilled状态
    这意味着如果我们在Promise中同时调用了resolvereject方法,那么只会执行最先调用的那个方法
    剩下的方法并不是无法执行,而是状态被锁死无法改变
    如上面的代码所示,控制台结果如下

结果

resolve传入值

  1. 传入一个普通对象或一个普通值
    这个值将会作为then方法的回调函数的参数,并且也会将这个Promise的状态置为fulfilled
  2. 传入一个Promise
    这个新的Promise的状态将会决定原来Promise的状态
  3. 传入一个对象,对象中有then方法
    也叫做thenable对象,执行这个then方法,并根据then方法的结果来决定Promise的状态

Promise的实例方法

Promise实例方法具体有以下三个

then

then方法接收两个参数,一个是成功的回调函数,一个是失败的回调函数

var promise = new Promise((resolve, reject) => {
    resolve("success")
    reject("fail")
})
promise.then((res) => {
    console.log(res)
}, (err) => {
    console.log(err)
})

这种写法等价于上面演示的写法

一个Promisethen方法可以多次调用

var promise = new Promise((resolve, reject) => {
    resolve("success")
})
promise.then((res) => {
    console.log("res1", res)
})
promise.then((res) => {
    console.log("res2", res)
})

结果如下
结果

then的返回值

then方法也具有返回值,他会返回一个Promise
因为返回的是一个Promise所以能进行链式调用
而如果想要继续调用接下来的then方法就需要在这个Promise中调用resolve方法
resolve方法可以传入参数,这里的参数就是最开始的then方法的回调函数的返回值

有以下三种情况

  1. 回调函数返回一个普通值

    var promise = new Promise((resolve, reject) => {
        resolve("success")
    })
    promise.then((res) => {
        console.log(res)
        return "aaa"
    }).then((res) => {
        console.log(res)
    })
    

    相当于then方法返回一个PromisePromise调用了resolve方法,resolve方法将aaa作为参数传递给了下一个then方法
    控制台结果如下
    结果

  2. 回调函数返回一个Promise

    var promise = new Promise((resolve, reject) => {
        resolve("success")
    })
    promise.then((res) => {
        console.log(res)
        return new Promise((resolve, reject) => {
            resolve("aaa")
        })
    }).then((res) => {
        console.log(res)
    })
    

    如果返回的是一个Promise的会先等新的Promise调用resolve之后将resolve的结果传递给下一个then
    控制台结果同上

  3. 回调函数返回一个含有then方法的对象

    var promise = new Promise((resolve, reject) => {
        resolve("success")
    })
    promise.then((res) => {
        console.log(res)
        return {
            then: function (resolve) {
                resolve("aaa")
            }
        }
    }).then((res) => {
        console.log(res)
    })
    

    如果返回的是一个thenable对象的话会等resolve之后将对应的值返回给下一个then
    控制台结果同上

返回Promise的状态

在运行then中的回调函数Promise的状态为pending
在回调函数返回一个结果Promise的状态为fulfilled
then方法抛出一个异常Promise的状态为rejected

catch

catch方法和then方法一样可以多次调用

var promise = new Promise((resolve, reject) => {
    reject("error")
})
promise.catch((res) => {
    console.log("res1", res)
})
promise.catch((res) => {
    console.log("res2", res)
})

控制台结果如下
结果

catch的返回值

catch方法也会返回一个Promise,所以我们也能在catch后面链式调用thencatch
值得注意的是,catch的回调执行完后返回的Promise默认状态为fulfilled
即会在catch调用完后在接下来的链式调用中跳过catch调用then
如果想要调用接下来的catch则需要抛出一个异常

var promise = new Promise((resolve, reject) => {
    reject("error")
})
promise.catch((res) => {
    console.log("res1", res)
    throw new Error("error")
}).catch((res) => {
    console.log("res2", res)
    return "success"
}).catch((res) => {
    console.log("res3", res)
    return "error"
}).then((res) => {
    console.log("res4", res)
})

控制台结果如下
结果

finally

finally是在ES9中新增的一个方法
表示无论Promise对象无论变成fulfilled还是rejected状态,最终都会执行的代码
finally方法不接收参数

var promise = new Promise((resolve, reject) => {
    resolve("success")
})
promise.catch((res) => {
    console.log("res1", res)
}).then((res) => {
    console.log("res2", res)
}).finally(() => {
    console.log("res3")
})

控制台结果如下
结果

Promise的类方法

Promise除了有实例方法之外还有类方法

resolve

resolve方法会将一个现成的内容转换成Promise来使用

var promise = Promise.resolve("success")
promise.then((res)=>{
	console.log(res)
})

相当于

var promise = new Promise((resolve, reject) => {
    resolve("success")
})
promise.then((res) => {
    console.log(res)
})

控制台结果如下
结果

reject

resolve类似,只不过会调用reject方法

var promise = Promise.reject("error")
promise.catch((res) => {
    console.log(res)
})

相当于

var promise = new Promise((resolve, reject) => {
    reject("error")
})
promise.catch((res) => {
    console.log(res)
})

控制台结果如下
结果

all

all方法会将多个Promise包裹成一个新的Promise新的Promise状态由包裹的所有Promise决定
当包裹的所有Promise的状态都为fulfilled时,新Promise的状态为fulfilled,并且会将包裹的所有Promise的返回值组成一个数组返回
如果包裹的Promise中有一个状态为reject时,新Promise状态为reject,并且会将第一个reject返回值返回

var p1 = new Promise((resolve, reject) => {
    resolve("p1 success")
})
var p2 = new Promise((resolve, reject) => {
    resolve("p2 success")
})
var p3 = new Promise((resolve, reject) => {
    reject("p3 error")
})
var p4 = Promise.all([p1, p2])
p4.then((res) => {
    console.log(res)
})
var p5 = Promise.all([p2, p3])
p5.catch((res) => {
    console.log(res)
})

控制台结果如下
结果

allSettled

allSettled方法和all方法类似,但不同的是allSettted方法会等待所有Promise有结果后才会有最终的状态,并且最终的状态一定为fulfilled

var p1 = new Promise((resolve, reject) => {
    resolve("p1 success")
})
var p2 = new Promise((resolve, reject) => {
    resolve("p2 success")
})
var p3 = new Promise((resolve, reject) => {
    reject("p3 error")
})
var p4 = Promise.allSettled([p1, p2, p3])
p4.then((res) => {
    console.log(res)
})

控制台结果如下
结果
其中statusPromise的状态,value为这个Promise的返回值

race

race同样将多个Promise组合成一个新Promise
在这些Promise如果有一个结果新Promise的状态就由这个有结果的Promise决定

var p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("p1 success")
    }, 2000);
})
var p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("p2 success")
    }, 3000);
})
var p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject("p3 error")
    }, 100);
})
var p4 = Promise.race([p1, p2, p3])
p4.then((res) => {
    console.log(res)
}).catch((res) => {
    console.log(res)
})

控制台结果如下
结果

any

race方法相似,不同的地方在于any方法会一直等到第一个fulfilled状态Promise出现再来决定新Promise的状态
如果所有的Promise均为reject就会报一个AggregateError的错误

var p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("p1 success")
    }, 2000);
})
var p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("p2 success")
    }, 3000);
})
var p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject("p3 error")
    }, 100);
})
var p4 = Promise.any([p1, p2, p3])
p4.then((res) => {
    console.log(res)
})
var p5 = Promise.any([p3])
p5.catch((res) => {
    console.log(res)
})

控制台结果如下
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值