Promise异步编程

一、回调函数

回调函数本身是同步代码
通常在编写JavaScript代码时,使用的回调嵌套的形式大多是异步函数,所以会导致部分开发者认为凡是回调形式的函数都是异步流程。其实并不是这样的,真正的解释是:JavaScript中的回调函数结构,默认是同步的结构,由于JavaScript单线程异步模型的规则,如果想要编写异步的代码,必须使用回调嵌套的形式才能实现,所以回调函数结构不一定是异步代码,但是异步代码一定是回调函数结构。
采用默认的上下结构永远拿不到异步回调中的结果,这也是为什么异步流程都是回调函数的原因 。

二、Promise介绍

注意:Promise是同步方法,promise.then是异步方法

 console.log('--开始----');
      const p = new Promise(function(resolve, reject) {
        console.log('调用Promise');
        // resolve可以触发then中的回调执行
        resolve('执行了resolve');
        // reject('异常了');
      });
      p.then(res => {
        console.log('then 执行', res);
      })
        .catch(err => {
          console.log('catch 执行', err);
        })
        .finally(() => {
          console.log('finally 执行');
        });
      console.log('--结束----');
      
      执行结果:
      --开始----
      调用Promise
      --结束----
      then 执行 执行了resolve
      finally 执行

通过上例,我们可以知道new Promise中的回调函数确实是在同步任务中执行的,其次是如果这个回调函数内部没有执行resolve或者reject那么p对象后面的回调函数内部都不会有输出,而运行resolve函数之后.then和.finally就会执行,运行了reject之后,catch和finally就会执行。

Promise的组成

每一个Promise对象都包含三部分
[[Prototype]]: 代表Promise的原型对象
[[PromiseState]]: 代表Promise对象当前状态
[[PromiseResult]]: 代表Promise对象的值,分别对应resolve或reject传入的结果

Promise的三种状态:

  • pending:初始状态,也叫就绪状态,这是在Promise对象定义初期的状态,这时Promise仅仅做了初始化并注册了他对象上所有的任务。
  • fulfilled:已完成,通常代表成功执行了某一任务,当初始化中的resolve执行时,Promise的状态就变更为fulfilled,并且then函数注册的回调函数会开始执行,resolve中传递的参数会进入回调函数作为形参。
  • rejected:已拒绝,通常代表执行了一次失败任务,或者流程中断,当调用reject函数时,catch注册的回调函数就会触发,并且reject中传递的内容会变成回调函数的形参。

Promise的链式调用

链式调用的本质就是在调用这些支持链式调用的函数的结尾时,又返回了一个包含他自己的对象或一个新的自己,这些方式都可以实现链式调用。

// 链式调用
function MyPromise() {
return this;
}
MyPromise.prototype.then = function() {
console.log('触发了then');
return this;
};
new MyPromise().then().then();

三、Promise的注意事项

链式调用

// 链式调用的注意事项
let pr = new Promise((resolve, reject) => {
    resolve('promise实例');
});
pr.then(res => {
    console.log('res'); // undefined
    return '123';
})
    .then(res => {
    console.log('res'); // 123
    return new Promise(resolve => {
        resolve(456);
    });
})
    .then(res => {
    console.log('res'); // 456
    return '直接返回的结果';
})
    .then()
    .then('字符串')
    .then(res => {
    console.log('res'); // 直接返回的结果
});

1、只要有then()并且触发了resolve,整个链条就会执行到结尾,这个过程中的第一个回调函数的参数是resolve传入的值
2、后续每个函数都可以使用retrun返回一个结果,如果没有返回结果的话,下一个then中回调函数的参数就是undefined
3、返回结果如果是普通变量,那么这个值就是一个then中回调函数的参数
4、如果返回的是一个Promise对象,那么这个Promise对象resolve的结果会变成下一次then中回调的函数的参数【直接理解为,返回Promise对象时,下一个then就是该对象的then】
5、如果then中传入的不是函数或者未传值 ,Promise链条并不会中断then的链式调用,并且在这之前最后一次的返回结果,会直接进入离它最近的正确的then中的回调函数作为参数

Promise中断

有两种形式可以让then的链条中断,如果中断还会触发一次catch的执行

// 链式调用的中断
      let pe = new Promise((resolve, reject) => {
        resolve('Promise的值');
      });
      pe.then(res => {
        console.log(res);
      })
        .then(res => {
          // 两种方式中断Promise
          // throw '通过throw中断';
          return Promise.reject('reject中断');
        })
        .then(res => {
          console.log(res);
        }).catch(res => {
          console.log('catch执行', res);
        });;

四、Promise常见的API

1. Promise.all()

当我们在代码中需要使用异步流程控制时,可以通过Promise.then来实现让异步流程一个接一个的执行,假设有三个接口,并保证三个接口的数据全部返回后,才能渲染页面。如果a耗时1s、b耗时0.8s、c接口耗时1.4s,如果用Promise.then来执行流程控制,如果通过then函数的异步控制,必须等待每个接口调用完毕才能调用下一个,这样总耗时就是 1+0.8+1.4=3.2s。这种累加显然增加了接口调用的时间消耗,所以Promise提供了一个all方法来解决这个问题:
Promise.all([promise对象,promise对象,...]).then(回调函数)
回调函数的参数是一个数组,按照第一个参数的promise对象的顺序展示每个promise的返回结果。
借助Promise.all来实现,等最慢的接口返回数据后,一起得到所有接口的数据,那么这个耗时将会只按照最慢接口的消耗时间1.4s执行,总共节省了1.8s.
Promilse.all相当于统一处理了多个promise任务,保证处理的这些所有promise对象的状态全部变成为fulfilled之后才会触发all的.then函数来保证将放置在all中的所有任务的结果返回

2.Promise.race()

Promise.race([promise对象,promise对象,...]).then(回调函数)
回调函数的参数是前面数组中最快一个执行完毕的promise的返回值。
Promilse.race()相当于将传入的所有任务进行了一个竞争,他们之间最先将状态变成fulfilled的那一个任务就会直接的触发race的.then函数并且将他的值返回,主要手于多个任务之间竞争时使用

五、Async和Await

async和await相当于使用了自带执行函数的Generator函数,所以async和await逐渐成为主流异步流程控制的终极解决方案。

    async handleTest() {
      console.log('1');
      // this.test();// await方法
      this.promisTest(); // 等价promise方法
      console.log('2');
      // 输出顺序:1,3,2,4
    },
    // await方法
    async test() {
      console.log(3);
      var a = await 4;
      console.log(a);
    },
    // 此方法是test方法的promsise写法
    promisTest() {
      new Promise(resolve => {
        console.log(3);
        resolve(4);
      }).then(res => {
        console.log(res);
      });
    }

async函数中有一个最大的特点,就是第一个await会作为分水岭一般的存在,在第一个await的右侧和上面的代码,全部是同步代码区域相当于new Promise的回调,第一个await的左侧和下面的代码,就变成了异步代码区域相当于then的回调。

总结

从回调地狱到Promise的链式调用到Generator函数的分步执行,再到async和await的自动异步代码同不化机制,经历了很长时间。Promise和事件循环系统并不是JavaScript中的高级知识,而是真正的基础知识。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容