ES6 Promise异步 笔记

    xiaoxiao2022-07-13  169

    首先 强烈推荐观看 阮大神的 SE6入门 里面关于promise的讲解更加深刻

    1,Promise is what ?

    Promise是异步编程的一种解决方案,

    所谓Promise,就是一个对象,用来传递异步操作的消息。说的通俗点 promise是一个承诺,承诺过一段时间就会给你一个结果

    Promise 对象有两个特点:

    promise对象状态不受外界影响。它有三种状态:Pending(进行中),Resolved(已完成,又称fulfiled),Rejected(失败) 这三种状态只有异步操作的结果可以决定单曲是那种状态,其他任何操作都无法改变当前状态。Promise状态一旦改变就不会再变,任何时候都可以得到这个结果,状态不可以逆,只能由 Pending变成Resolved或者由Pending变成Rejected

     2,Promise 解决的问题

    平时大家经常写这样的代码:

    {//es5 回调写法 let ajax = function(callback){ console.log('执行') setTimeout(function(){ callback&&callback.apply() },1000) } ajax(function(){ console.log('执行2') }) }

    类似这种一两层的回调函数还是可以忍受的,但是如果加入多次发生你的代码就会变成这个熊样。

    fun1(function(){ fun2(function(){ fun3(function( fun4(funciton(){ fun5(function(){ // - - }); }); )); }); }); 这就是所谓的回调地狱,代码层层嵌套,环环相扣,很明显,逻辑稍微复杂一些,这样的程序就会变得难以维护。 这时候我们的promise就应运而生了

    3,基础用法

    ES6规定,Promise对象是一个构造函数,用来生成Promise实例。它自身上有all、reject、resolve这几个眼熟的方法,原型上有then、catch等方法。

    先创造一个Promise对象:

    var promise = new Promise (function(resolve,reject){ //做一些异步操作 if(/*异步操作成功*/){ resolve(value) }else{ reject(err) } })

    Promise构造函数中接受一个函数作为参数,该函数的两个参数分别为reslove和reject。

    reslove:异步操作执行成功后的回调函数reject:异步操作执行失败后的回调函数 

    下面看这段代码:

    var promise = new Promise(function(resolve,reject){ // some code var data = '数据' setTimeout(function(){ console.log('执行完成') resolve(data) },5000) });

    上面的代码中,我们执行了一个异步操作,也就是setTimeout,5秒后,输出“执行完成”,并且调用resolve方法。

    运行代码,会在5秒后输出“执行完成”。但是 上面我们知识new了一个对象并没有去调用它。所以我们用promise时候一般会包在一个函数中,在需要的时候去调用该函数。下面看下调用的api。

    function ajax(){ var promise = new Promise(function(resolve,reject){ // some code var data = '数据' setTimeout(function(){ resolve(data) },5000) }); return promise; } ajax()

    上面代码中包裹好了promise 直接运行这个函数的话会返回一个promise对象。

    现在使用promise带的 方法去实现调用

    function ajax(){ //创一个promise方法 var promise = new Promise(function(resolve,reject){ // some code var data = '数据' setTimeout(function(){ resolve(data) },5000) }); return promise; } ajax().then(value=>{//promise调用方式 console.log(value) })

    then里面的函数就跟我们平时的回调函数一个意思,能够在ajax()这个异步任务执行完成之后被执行。这就是Promise的作用了,简单来讲,就是能把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。

    Promise.prototype.then()链式操作

    Promise 的优势在于,可以在then中继续写promise对象并返回。然后继续调用then来进行回调操作。。。

    function runAsync1 (){ return new Promise ((resolve,reject)=>{ setTimeout( a =>{ console.log('异步数据完成1') resolve('数据1') },2000) }) } function runAsync2 (){ return new Promise ((resolve,reject)=>{ setTimeout( a =>{ console.log('异步数据完成2') resolve('数据2') },2000) }) } function runAsync3 (){ return new Promise ((resolve,reject)=>{ setTimeout( a =>{ console.log('异步数据完成3') resolve('数据3') },2000) }) } //链式调用 runAsync1().then(data=>{ console.log(data) return runAsync2() }).then(data=>{ console.log(data) return runAsync3() }).then(data=>console.log(data))

    运行后发现输出为:

    异步数据完成1 数据1 异步数据完成2 数据2 异步数据完成3 数据3

    在then方法中,也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了

    Promise.reject的用法 

    到这里,你应该对“Promise是什么玩意”有了最基本的了解。前面的小例子中都是只有成功状态没有失败状态,

    reject的作用就是吧Promise的状态设置为rejected,这样我们就能捕获到 然后执行失败的回调。

    看例子:

    var runAntys= function(){ return new Promise(function(resolve,reject){ var num = parseInt(Math.random()*2) console.log(num) if(num<1){//async resolve() }else{ reject() } }) } ceshi() .then(function(){ console.log('成功') },function(err){ console.log('err') })

    测试函数用来获取一个异步的的数字,这个随机数在0-2之间,小于1成功的话会走resolve()如果大于1的话我们认为它是失败 改变reject的状态 

    then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。所以我们能够分别拿到他们传过来的数据。

    Promise.prototype.catch(),发生错误的回调函数。

    catch其实它和then的第二个参数一样,用来指定reject的回调。

    p.then((data) => { console.log('resolved',data); }).catch((err) => { console.log('rejected',err); });

    效果和then第二个参数一样,不过他的另一个作用,在执行resolve的回调时候抛出异常了,并不会报错卡死js,而是会进入catch方法中。

    这个与try/catch语句有相同的功能。

    Promise.all()  并行执行异步操作

    谁跑的慢,以谁为准执行回调。all接收一个数组参数,里面的值最终都算返回Promise对象。

    let Promise1 = new Promise(function(resolve, reject){}) let Promise2 = new Promise(function(resolve, reject){}) let Promise3 = new Promise(function(resolve, reject){}) let p = Promise.all([Promise1, Promise2, Promise3]) p.then(funciton(){ // 三个都成功则成功 }, function(){ // 只要有失败,则失败 })

    all 的应用场景还是很多的,比如 素材比较多的网页。打开后预先加载需要各种资源的图片 flash或者静态文件,我们用all等他们全部加载完成后,再让页面进行初始化。

    Promise.race()  谁跑的快,以谁为准执行回调

    举例 用settimeout去做响应时间看效果

    {//打飞机比赛 function runAsync1 (){ return new Promise ((resolve,reject)=>{ setTimeout( a =>{ resolve('老佳同学') },1000) }) } function runAsync2 (){ return new Promise ((resolve,reject)=>{ setTimeout( a =>{ resolve('小樊凡同学') },2000) }) } function runAsync3 (){ return new Promise ((resolve,reject)=>{ setTimeout( a =>{ resolve('王老五同学') },3000) }) } let race = Promise.race([runAsync1(),runAsync2(),runAsync3()]) race.then((data)=>{//race代表 竞赛关系 第一名胜出 console.log('第一名是:'+data) }).finally(() => console.log('每个同学都很厉害 都没超过三秒')) //ES9 Promise 新增api 无论 promise 的执行成功或失败都会去执行 }

    这三个异步操作同样是并行执行的。结果你应该可以猜到,1秒后runAsync1已经完了,此时then里面的就执行最先完成的异步。


    我们还可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作。

    { function getImgSrc(){ return new Promise((resolve,reject)=>{ var img = new Image(); img.onload = function(){ resolve(img) } img.src = '错误的地址' //img.src = 'http://pic.58pic.com/58pic/15/68/59/71X58PICNjx_1024.jpg' }) } function timeOut (){ return new Promise ((resolve,reject)=>{ setTimeout(function(){ reject('图片获取失败') },5000) }) } let getimg = Promise.race([getImgSrc(),timeOut()]) getimg.then(data=>{ console.log(data) }).catch(data=>{ console.log(data) }) }

    上面代码中做了一个 getimgsrc会异步的获取图片,假设把scr设置为错误的地址,肯定是拿不到图片数据的,timout函数是一个延时函数。我们吧这两个函数放入 race中,让他们赛跑。如果五秒钟内图片请求成功了。那么进入 then 正常加载。如果超过五秒还没有返回成功。那么进入catch 并且抛出 图片获取失败。

     

    Promise.prototype.finally() [ES9 语法]

    上面例子中也有使用过

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

    fetch('file.json') .then(data => data.json()) .catch(error => console.error(error)) .finally(() => console.log('finished'))

    总结:按我自己的理解说一下promise,就是你执行一个函数,如果他执行成功了,他就执行resolve函数,如果失败了就执行reject函数 

    最新回复(0)