Java教程

Promise

本文主要是介绍Promise,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

其实有的地方还没有整理到,但是先整理到这啦,后面还有去看看别的题呢嘿嘿

目录

1 异步的产生

2 传统的异步模式

2.1 事件处理

2.2 回调函数

3 传统异步模式的问题 

3.1 回调地狱

3.2 异步之间的联系

4 Promise  

4.1 Promise的状态

4.2 Promise的用途

4.3 如何控制Promise的状态(函数参数,静态方法)

 4.4 Promise的实例方法

4.5 两个合成Promise的静态方法

5 异步函数(async/await)


1 异步的产生

当代码要访问一些高延迟的资源,比如向远程服务器发送请求并等待响应,这个时候就需要长时间的等待。因为强制进程等待一个长时间的操作通常是不可行的,但是同步操作就必须等待,因此就需要异步操作和同步操作的配合。所以异步行为就是为了优化因计算量大而时间长的操作。

2 传统的异步模式

2.1 事件处理

某个对象的属性是一个函数,当发生某一件事时,运行该函数。

dom.onclick = function(){

}

2.2 回调函数

运行某个函数以实现某个功能的时候,传入一个函数作为参数,当发生某件事的时候,会运行该函数。 

dom.addEventListener("click", function(){

})

3 传统异步模式的问题 

3.1 回调地狱

所谓“回调地狱”,其实也就是嵌套回调。即异步返回的值又依赖另一个异步的返回值,那么回调就会变得更加复杂,外观上也更加繁琐,代码很难维护。

    <div>
        <button id="btn1">给按钮2注册点击事件</button>
        <button id="btn2">给按钮3注册点击事件</button>
        <button id="btn3">点击后弹出"hello"</button>
    </div>
    <script>
        let btn1 = document.getElementById('btn1'),
            btn2 = document.getElementById('btn2'),
            btn3 = document.getElementById('btn3');
        btn1.addEventListener('click',function(){
            btn2.addEventListener('click',function(){
                btn3.addEventListener('click',function(){
                    alert('hello')
                })
            })
        })
    </script>

比如上面这个代码,点击按钮3就需要依赖按钮2,按钮2就需要依赖按钮1,它们之间就形成了嵌套,如果三个按钮变成了一百个甚至更多个,代码将会变得更加地丑陋,而且很难维护,层层嵌套,形成了“回调地域”。

3.2 异步之间的联系

 某个异步操作要等待多个异步操作的结果,对这种联系的处理,会让代码的复杂度剧增

4 Promise  

4.1 Promise的状态

Promise可能处于3种状态:pending(待定),fulfilled(兑现),rejected(拒绝)。pending(待定)是Promise的最初状态,它可以settled(落定)为代表成功的fulfilled状态,也可以settled为代表失败的rejected状态。Promise的状态从pending转换为fullfilled或者rejected状态之后,状态就不可以再改变了。

4.2 Promise的用途

(1)可以通过Promise状态的转换来判断异步代码是否完成。

(2)在一些情况下,Promise封装的异步操作会实际生成某个值,程序希望可以获取到这个值。

为了实现这两种用途,在状态进行转换的时候,如果是fulfilled,那么会有一个值(value);如果是rejected,那么会有一个理由(reason)。可以进行使用。

4.3 如何控制Promise的状态(函数参数,静态方法)

可以通过调用Promise的两个函数参数来控制状态的转换,也可以通过调用Promise.resolve()静态方法。

(1)通过执行函数控制Promise状态

let p1 = new Promise((resolve,reject) => resolve())
        console.log(p1)

上面的代码就会把Promise转换为fulfilled状态,调用reject()就会转换为rejected状态。

(2)通过调用Promise.resolve()和Promise.reject()静态方法

let p2 = Promise.resolve()
        console.log(p2)

上面这段代码同样的把Promise转换为fulfilled状态,它们的输出结果是相同的。因此实际上是一样的。

说一下Promise.solve()和Promise.reject()不同的地方

首先,这两个静态方法都可以使Promise转换状态,但是不同的是,当传入的参数也是一个Promise时,Promise.resolve()的行为类似于一个空包装。但是Promise.reject()传入Promise参数时,这个传入的Promise会成为塔返回的拒绝Promise的理由。为了方便理解,代码如下:

let p = Promise.resolve(5)
console.log(Promise.resolve(p))
console.log(Promise.reject(p))

 4.4 Promise的实例方法

在异步代码执行完了之后,对于fulfilled和rejected两种结果分别返回的value和reason,可以通过Promise的实例方法来进行处理,因此,Promise的实例方法是连接外部同步代码和内部异步代码之间的桥梁。它们可以访问异步操作返回的数据并进行处理。

(1)Promise.prototype.then()

这个方法是为Promise实例添加处理程序的主要方法。而且这个方法返回一个新的Promise实例。最多可以接收两个参数:onResolved处理程序和onRejected处理程序。这两个参数都是可选的,可以提供也可以不提供,要是提供的话,onResolved在Promise进入fulfilled状态时执行,onRejected在Promise进入rejected状态时执行。要是不提供这两个参数的话,Promise.resolve()就会包装上一个Promise解决之后的值。

首先,代码看看这个方法的实现原理:

        function onResolved(id){
            setTimeout(console.log,0,id,'resolved')
        }
        function onRejected(id){
            setTimeout(console.log,0,id,'rejected')
        }
        let p1 = new Promise((resolve,reject) => resolve())
        let p2 = new Promise((resolve,reject) => reject())
        p1.then(() => onResolved('p1'),
                () => onRejected('p1'))
        p2.then(() => onResolved('p2'),
                () => onRejected('p2'))

上面的程序也可以只提供onRejected参数,需要在onResolved的位置上传入undefined 

使用这个Promise.then()方法的几种情况:

a.调用then()时不传处理程序,原样向后传。像下面这样,p2调用then()的时候什么都没有传,那么p2的状态还有value都和p1是一样的。

        let p1 = Promise.resolve('test')
        let p2 = p1.then()
        console.log(p2)

b.调用then()的时候传了参数,但是没有显式的返回语句,像下面这种,那么就会返回undefined,状态仍然是fulfilled。

        let p1 = Promise.resolve('test')
        let p2 = p1.then(() => undefined)
        let p3 = p1.then(() => {})
        let p4 = p1.then(() => Promise.resolve());
        console.log(p2,p3,p4)

c.如果调用的时候有显式的返回值,那么就会返回这个值。这种情况和前面的是一样的,只不过返回的值就是调用then()的时候传入的值。(传入的是Promise.reject()的话状态也会变到rejected)就不写代码啦。

要是抛出错误(p.then(() => {throw 'test'})的话,返回的就是rejected状态的promise,要是返回错误值(p.then(() => Error('test'))的话,返回的仍然是fulfilled状态的promise。 

 调用onRejected处理程序的时候和上面的情况时类似的,onRejected处理程序返回的值也被Promise.resolve()包装。因为其实在进入rejected状态之后,代表捕获了错误,因此相当于是符合预期的行为,返回一个fulfilled的Promise。

(2)Promise.prototype.catch() 

其实这个方法吧,相当于是promise.then()方法第一个参数为null,传入第二个参数(调用onRejected处理程序)。返回的也是一个新的Promise。

(3)Promise.prototype.finally()

作用是避免onResolved和onRejected处理程序中出现冗余代码。返回的也是一个新的Promise。 

这个方法与状态无关。不管是fulfilled还是rejected状态,大多都表现为父Promise状态的传递。如果传入的是pending状态或者rejected状态的Promise,会返回相应的Promise。

4.5 两个合成Promise的静态方法

Promise.all()和Promise.race()是Promise类提供的将多个Promise实例组合成一个Promise的静态方法。

Promise.all()是一组Promise全部都转换到fulfilled状态之后才会转换到fulfilled状态;

(1)要是一组里面都是解决的Promise,那么返回的就是所有包含value的数组。

let p = Promise.all([Promise.resolve(1),Promise.resolve(2),Promise.resolve(3)])
console.log(p)

(2)要是一组里面出现了一个拒绝的Promise,那么返回的Promise就进入rejected状态,reason就是拒绝的那个Promise的reason

        let p = Promise.all([Promise.resolve(1),Promise.resolve(2),Promise.reject(3)])
        console.log(p)

Promise.race()就类似于比赛,一组Promise中只要有一个Promise转换到fulfilled或者rejected状态,那么就结束了。Promise.race()只要第一个解决或者拒绝的Promise出现就可以,新的Promise的状态以及value/reason就是第一个落定的这个Promise的。代码就不写啦,和前面的一个道理。

!!注意:Promise.all()和Promise.race()在使用的时候,语法都要像下面这样:

let p1 = Promise.all([Promise.resolve(),Promise.resolve(),Promise.reject()])
let p1 = Promise.race([Promise.resolve(),Promise.reject()])

 要是空的可迭代对象,要这样写:

let p1 = Promise.race([])

要是这样,语法就是无效的。 

let p1 = Promise.race()

即调用这两个方法的时候,要把传入的Promise放到一个[]里面。

5 异步函数(async/await)

异步函数(async/await)是ES8规范新增的,是ES6的Promise在函数中的应用。

async/await让以同步方式写的代码可以异步执行。

1.async

async关键字用于声明异步函数。可以用在函数声明、函数表达式、箭头函数和方法上:

async function test(){}//函数声明
let test = async function() {}//函数表达式
let test = async () => {}//箭头函数
class Test(){
    async test(){}//方法
}

像上面这样,在使用async声明之后,函数就具有了异步的特征。对于参数、闭包,异步函数仍然具有普通js函数的正常行为, 像下面这个代码,函数虽然有了异步的特征,但是代码还是同步求值的:

        async function test(){
            console.log(1)
        }
        test();
        console.log(2)

因此上面的输出结果还是按正常的同步代码的顺序的。

但是,在被async声明为异步函数之后,如果函数内部使用了return关键字返回了值,(没有return会返回undefined),这个值会被Promise.resolve()包装成一个Promise对象。在函数外部调用可以得到它返回的Promise:

        async function test(){
            console.log(1)
            return 3;//这句代码等同于:return Promise.resolve(3),返回的结果都是一样的
        }
        test().then(value => console.log(value));
        console.log(2)

 

同样的,如果把return一个值换成抛出一个错误(throw 3),那么返回的就是一个拒绝的promise。

        async function test(){
            console.log(1)
            throw 3
        }
        test().catch(reason => console.log(reason))
        console.log(2)

 

这种情况下抛出的错误会被异步函数捕获。但是,拒绝Promise的错误不会被异步函数捕获,像下面这样: 

        async function test(){
            console.log(1)
            Promise.reject(3)
        }
        test().catch(reason => console.log(reason))
        console.log(2)

 

 2.await

await关键字可以暂停异步函数代码的执行,等待Promise解决。  await必须在异步函数中使用,即有await就有async。然后Promise解决完之后把值传给表达式,再回复异步函数的执行。可以单独使用,也可以在表达式中使用: 

        async function test(){
            console.log(await Promise.resolve(1))
        }
        test()
        async function demo(){
            return await Promise.resolve(1)
        }
        demo().then(console.log)

 

同样的,如果await等待的是一个会抛出错误的同步操作,会返回拒绝的promise。

        async function test(){
            console.log(1)
            await (() => {throw 3;})()
        }
        test().catch(console.log)
        console.log(2)

 

在前面的async部分一样,拒绝的promise不会被异步函数捕获,会抛出错误。对拒绝的promise使用await就可以释放错误值。

        async function test(){
            await Promise.reject(2)
        }
        test().catch(console.log)

 

async/await中真正起作用的是await。异步函数如果不包含await关键字,其执行基本上跟普通函数没什么区别。js运行时,碰到await时,会记录在哪里暂停执行,然后等待await后面的值可用,再继续恢复异步函数的执行。

await这行代码后面的部分会被异步求值:

        async function test(){
            console.log(1)
            console.log(await Promise.resolve(2));
            console.log(3)
        }
        test()
        console.log(4)

 

 

 

 

 

 

 

 

 

 

 

 

这篇关于Promise的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!