ES6学习笔记篇五之Promise

Promise概述

1、异步处理的通用模型

ES6 将某一件可能发生异步操作的事情,分为两个阶段:unsettledsettled

  • unsettled:未决阶段,表示事情还在进行前期的处理,并没有发生通向结果的那件事

  • settled:已决阶段,事情已经有了一个结果,不管这个结果是好是坏,整件事情无法逆转

异步操作总是从 未决阶段 逐步发展到 已决阶段的。并且,未决阶段拥有控制何时通向已决阶段的能力。

异步操作分成了三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)

  • pending:进行中,处于未决阶段,则表示这件事情还在进行(最终的结果还没出来)
  • fulfilled:已成功,已决阶段的一种状态,表示整件事情已经出现结果,并是一个可以按照正常逻辑进行下去的结果
  • rejected:已失败,已决阶段的一种状态,表示整件事情已经出现结果,并是一个无法按照正常逻辑进行下去的结果,通常用于表示有一个错误

一旦这种状态改变,就会固定,不会再改变。是不可逆的Promise状态的改变只有两种情况:

  • 1、从pending通过resolve()变为fulfilled
  • 2、从pending通过reject()变为rejected

参考流程图

在这里插入图片描述

2、Promise及其基本使用

为了解决地狱回调和异步操作之间的联系,ES6提出了一种异步编程大的解决方案Promise。但是Promise并没有消除回调,只是让回调变得可控。

Promise是一个对象,它可以获取异步操作的消息。为了方便和简易,下面的resolved统指fulfilled状态

const pro = new Promise((resolve, reject)=>{
    /*
        未决阶段的处理
        通过调用resolve函数将Promise推向已决阶段的resolved状态
        通过调用reject函数将Promise推向已决阶段的rejected状态
        resolve和reject均可以传递最多一个参数,表示推向状态的数据
    */ 
    if(true){
        resolve()
    }else{
        reject()
    }
    
})
pro.then(data=>{
    /*
        这是thenable函数,如果当前的Promise已经是resolved状态,该函数会立即执行
        如果当前是未决阶段,则会加入到作业队列,等待到达resolved状态后执行
        data为状态数据
    */
},err=>{
    /*
    	then函数也可以填catchable函数,也可以不填。我们最好是通过catch方法添加catchable函数
    */
})
pro.catch(err=>{
   /*
        这是catchable函数,如果当前的Promise已经是rejected状态,该函数会立即执行
        如果当前是未决阶段,则会加入到作业队列,等待到达rejected状态后执行
        err为状态数据
    */
})

注意

  • Promise创建后会立即执行。

  • thenablecatchable函数是异步的,就算是立即执行,也会加入到事件队列中等待执行,加入的队列是微队列。

  • 在未决阶段的处理函数中,如果发生未捕获的错误,会将状态推向rejected,并会被catchable捕获。

  • 一旦状态推向了已决阶段,无法再对状态做任何更改。

  • Promise并没有消除回调,只是让回调变得可控。

const pro = new Promise((resolve,reject)=>{
    console.log("a")
    setTimeout(() => {
        console.log("d")
    }, 0);
    resolve(1)
    console.log("b")
})
pro.then(data=>{
    console.log(data)
})
console.log("c")
//a b c 1 d

以上代码,立即创建一个Promise函数,并且立即执行函数中的代码,所以首先输出a,随后将setTimeout中代码加入宏队列,然后通过resolve()Promise的状态推向已决状态,但是resolve也是异步的,他会加入到微队列中等同步代码执行完毕后再执行。随后输出bPromise函数执行完后,又执行剩下的同步代码输出c,同步代码执行完毕后,执行微队列中的输出resolve的结果值1,再执行宏队列中的setTimeout,输出b

3、Promise的方法then,catch,finally

(1)then()和catch()

then():注册一个后续处理函数,当Promise为resolved状态时运行该函数

catch():注册一个后续处理函数,当Promise为rejected状态时运行该函数

Promise对象中,无论是then方法还是catch方法,它们都具有返回值,返回的是一个全新的Promise对象,它的状态满足下面的规则:

  1. 如果当前的Promise是未决的,得到的新的Promise是进行中状态
  2. 如果当前的Promise是已决的,会运行响应的后续处理函数,并将后续处理函数的结果(返回值)作为resolved状态数据,应用到新的Promise中;如果后续处理函数发生错误,则把返回值作为rejected状态数据,应用到新的Promise中。

注意:后续的Promise一定会等到前面的Promise有了后续处理结果后,才会变成已决状态

如果前面的Promise的后续处理,返回的是一个Promise,则返回的新的Promise状态和后续处理返回的Promise状态保持一致。

const pro1 = new Promise((resolve,reject)=>{
    resolve(1)
})
console.log(pro1)
// 异步调用,
const pro2 = pro1.then(result => result *2)
console.log(pro2)//pro2是一个Promise对象,状态是pending 
pro2.then(result=>{console.log(result)},err=>console.log(err))

上述代码,在执行console.log(pro2)的时候是同步执行, 此时pro1.then()还未执行完毕,所以promise还是pending状态

const pro1 = new Promise((resolve,reject)=>{
    throw 1
})
const pro2 = pro1.then(result => result *2,err=> err*3)
pro2.then(result=>{console.log(result*2)},err=>console.log(err*3))
//6

上述代码,对于串联的Promise,then和catch均返回一个全新的Promise,所以在pro1的catch执行时返回的pro2执行的是正常的,并非抛出错误。所以执行的为err * 3result * 2

const pro1 = new Promise((resolve,reject)=>{
    throw 1
})
const pro2 = pro1.then(result => result *2,err=> err*3)
pro1.catch(err=>err*2)
pro2.then(result=>{console.log(result*2)},err=>console.log(err*3))

上述代码,每一次返回都是一个全新的Promise,所以pro1.catch(err=>err*2)并没有变量接收。

const pro1 = new Promise((resolve,reject)=>{
    resolve(1)
})
const pro2 = new Promise((resolve,reject)=>{
    resolve(2)
})
const pro3 = pro1.then(result=>{
    console.log(`结果${result}`) //1
    return pro2
})
//pro3的状态是pending
pro3.then(result=>{
    console.log(result)//2
})

上述代码,第一个then方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化后再执行

const pro1 = new Promise((resolve,reject)=>{
    resolve(1)
})
const pro2 = new Promise((resolve,reject)=>{
    setTimeout(() => {
        resolve(2)
    }, 3000);
})
pro1.then(result=>{
    console.log(`结果${result}`) //1
    return pro2
}).then(result=>{
    console.log(result)//2
}).then(result=>{
    console.log(result)//undefined
})

上面代码,最后一个输出undefined是因为第二个then方法没有返会值。

(2)finally()

finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

const pro1 = new Promise((resolve,reject)=>{
    resolve(1)
})
pro1.then(result=>{
    console.log(`结果${result}`) //结果1
    return 3
}).then(result=>{
    console.log(result)//3
}).catch(error=>{
	console.log(error)
}).finally(()=>{
	console.log("我一定会执行的")//我一定会执行的
})

4、Promise的静态成员

(1)resolve()

该方法返回一个resolved状态的Promise,传递的数据作为状态数据。有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用。

const pro = Promise.resolve(1)
//等同于
const pro = new Promise((resolve,reject)=>{
    resolve(1)
})

特殊情况:如果传递的数据是Promise,则直接返回传递的Promise对象

(2)reject()

该方法返回一个rejected状态的Promise,传递的数据作为状态数据

const pro = Promise.reject(1)
//等同于
const pro = new Promise((resolve,reject)=>{
    reject(1)
})

(3)all()

Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口。这个方法返回一个新的Promise对象,

返回的新的Promise对象的状态分成两种情况:

  1. 当参数中所有的Promise对象的状态都变成fulfilled,返回的Promise状态才会变成fulfilled。此时参数的返回值组成一个数组,传递给新Promise的回调函数。
  2. 当参数之中有一个被rejected,新Promise的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给新Promise的回调函数。
//p1, p2, p3均是promise对象
const pro = Promise.all([p1, p2, p3]);

上述代码就是一个非常简单的调用方法

function getRandom(min,max){
    return Math.random() * (max-min) + min
}
const proms = []
for(let i=0;i<10;i++){
    proms.push(new Promise((resolve,reject)=>{
        setTimeout(() => {
            if(Math.random()<0.1){
                reject(i)
            }else{
                resolve(i)
                console.log(i,"完成")
            }
        }, getRandom(1000,5000))
    }))
}
const pro = Promise.all(proms)
pro.then(res=>{
    console.log("全部完成",res)
},err=>{
    console.log(err,"错误")
})

上面代码是产生10个Promise对象,每个Promise对象都延迟时间1~5s中的随机时间后将状态推向已决,调用all方法,当10个Promise对象全部完成后再输出,或有一个错误的时候,也输出。

(4)race()

Promise.race()方法同样是将多个 Promise 对象,包装成一个新的 Promise 对象。Promise.race()方法的参数与Promise.all()方法一样。

当参数里的任意一个Promise被成功或失败后,新Promise马上也会用参数中那个promise的成功返回值或失败详情作为参数调用新promise绑定的相应句柄,并返回该promise对象

function getRandom(min,max){
    return Math.random() * (max-min) + min
}
const proms = []
for(let i=0;i<10;i++){
    proms.push(new Promise((resolve,reject)=>{
        setTimeout(() => {
            if(Math.random()<0.1){
                reject(i)
            }else{
                resolve(i)
                console.log(i,"完成")
            }
        }, getRandom(1000,5000))
    }))
}
const pro = Promise.race(proms)
pro.then(res=>{
    console.log("有完成的",res)
},err=>{
    console.log(err,"有错误")
})
console.log(proms)

上述代码和all()例子一样,只不过,当有一个完成或失败的时候,就会执行prothencatch回调函数

例子:异步加载图片

const preloadImage = function (path) {
  return new Promise(function (resolve, reject) {
    const image = new Image();
    image.onload  = resolve();
    image.onerror = reject();
    image.src = path;
  });
};

一旦图片加载完成,就会Promise状态就会发生变化。

6、async 和 await

async 和 await 是 ES2016 新增两个关键字,它们借鉴了 ES2015 中生成器在实际开发中的应用,目的是简化 Promise api 的使用,并非是替代 Promise。实际上就是生成器函数的一个语法糖。目的是简化在函数的返回值中对Promise的创建。

(1)async

async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

async 用于修饰函数(无论是函数字面量还是函数表达式),放置在函数最开始的位置,被修饰函数的返回结果一定是 Promise 对象。

async function test(){
    console.log(1);
    return 2;//完成时状态数据
}
//等同于
function test(){
    return new Promise((resolve, reject)=>{
        console.log(1);
        resolve(2);
    })
}
async function test(){
    console.log(1)
    return 2
}
const pro = test()
console.log(pro)//promise对象 promiseValue是2

注意async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。

(2)await

正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。await类似于生成器的yield,当遇到await的时候,就会等待await后面的Promise对象执行完毕后再继续执行下面代码。

async function test(){
    const namePro = await getName();//异步ajax
    const passwordPro = await getPassword();
}

test()函数执行过程中,会先等待getName()执行完毕后,再执行getPassword()

注意await关键字必须出现在async函数中

如果多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。

async function test(){
    let namePro = getName();
    let passwordPro = getPassword();
    let name = await namePro;
    let password = await passwordPro;
}

先让getName()getPassword()执行,然后再等待结果

await用在某个表达式之前,如果表达式是一个Promise,则得到的是thenable中的状态数据。

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

async function test2(){
    const result = await test1();
    console.log(result);
}

test2();
//等同于
function test1(){
    return new Promise((resolve, reject)=>{
        console.log(1);
        resolve(2);
    })
}
function test2(){
    return new Promise((resolve, reject)=>{
        test1().then(data => {
            const result = data;
            console.log(result);
            resolve();
        })
    })
}
test2();

如果await的表达式不是Promise,则会将其使用Promise.resolve包装后按照规则运行

async function test(){
    const result = await 1
    console.log(result)
}
//---等同
function test(){
    return new Promise((resolve,reject)=>{
        Promise.resolve(1).then(data =>{
            const result = data
            console.log(result)
            resolve()
        })
    })
}
最后附一个自己模仿封装的Promise
const MyPromise = (()=>{
    const PENDING = "pending",
    RESOLVE = "resolve",
    REJECT = "reject",
    PromiseValue = Symbol("PromiseValue"),
    PromiseStatus = Symbol("PromiseStatus"),
    changeStatus = Symbol("changeStaus"),
    thenables = Symbol("thenables"),
    catchables = Symbol("catchables"),
    settleHandle = Symbol("settleHandle"),
    linkPromise = Symbol("linkPromise");
    return class{
        //改变状态
        [changeStatus](newStatus,newValue,queue){
            if(this[PromiseStatus !== PENDING]) return
            this[PromiseStatus] = newStatus
            this[PromiseValue] = newValue       
           //执行相应队列中的函数
           queue.forEach(handler => handler(newValue));

        }
        /*
            executor未决阶段(pending状态)下的处理函数
        */ 
        constructor(executor){
            this[PromiseStatus] = PENDING;
            this[PromiseStatus] = undefined;
            this[thenables] = [] //resolved后续处理函数的数组
            this[catchables] = []//rejected后续处理函数的数组
            const resolve = data=>{
                this[changeStatus](RESOLVE,data,this[thenables])
            }
            const reject = (reason)=>{
                this[changeStatus](REJECT,reason,this[catchables])
            }
            try{
                executor(resolve,reject)
            }catch(err){
                reject(err)
            }
        }
        // 处理后续处理函数 后续处理函数,需要立即执行的状态,作业队列
        [settleHandle](handler,isNow,queue){
           if(typeof handler !== "function")return
            if(this[PromiseStatus] === isNow){
                setTimeout(() => {
                    handler(this[PromiseValue])
                }, 0);
            }else{
                queue.push(handler)
            }
        }
        [linkPromise](thenable,catchable){
            return new MyPromise((resolve,reject)=>{
                this[settleHandle](data=>{
                    try {
                        const result = thenable(data)//得到当前Promise
                        resolve(result)

                    } catch (error) {
                        reject(error)
                    }
                    },RESOLVE,this[thenables])
                this[settleHandle](err=>{
                   try {
                    const result = catchable(err)
                    resolve(result)
                   } catch (error) {
                       reject(error)
                   }
                },REJECT,this[catchables])

            })
        }
        then(thenable,catchable){
            return this[linkPromise](thenable,catchable)
        }
        catch(catchable){
            return this[linkPromise](undefined,catchable)
        }

        static all(prms){
            return new MyPromise((resolve,reject)=>{

            })
        }
        static race(prms){}
        static reject(reason){
            if(reason instanceof MyPromise){
                return reason
            }else{
                return new MyPromise(reject=>{
                    reject(reason)
                })
            }
        }
        static resolve(data){
            if(data instanceof MyPromise){
                return data
            }else{
                return new MyPromise(resolve=>{
                    resolve(data)
                })
            }
        }

    }
})()
const pro = new MyPromise((resolve,reject)=>{
    console.log("未决阶段")
    // throw new Error("111")
    resolve(1)
})
pro.then(data=>{
    console.log(data)
    return 234
}).then(data=>{
    console.log(data)
})

pro.catch(data=>{
    console.log(data)
})
pro.catch(data=>{
    console.log(data)
})
console.log(pro)
发布了21 篇原创文章 · 获赞 82 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Newbie___/article/details/104493811