什么是 promise?它的作用是什么?

首先说一下promise 是什么?

1、本质是构造函数中主要用于异步计算

2、可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果

其次主要处理异步和操作同步化

promise 有三个状态:

1、pending[待定]初始状态

2、fulfilled[实现]操作成功

3、rejected[被否决]操作失败

最后我们通过new 创建promise,而实例化之后的promise对象的参数是一个回调函数,回调函数有两个参数,分别为成功回调 resolve 和失败回调 reject ,通过原型上 then() 方法来接收成功之后的数据

异步操作的常见语法

        let str = new Promise(function(resolve, reject) {

            //做一些异步操作

            setTimeout(function() {

                console.log('执行完成Promise');

                resolve("");   //  要返回的数据可以任何数据例如接口返回数据

            }, 2000);

        })

console.log(str);

事件监听

document.getElementById('#start').addEventListener('click', start, false);

function start() {

  // 响应事件,进行相应的操作

}

// jquery on 监听

$('#start').on('click', start)

回调

// 比较常见的有ajax

$.ajax('http://www.wyunfei.com/', {

success (res) {

  // 这里可以监听res返回的数据做回调逻辑的处理

}

})

// 或者在页面加载完毕后回调

$(function() {

// 页面结构加载完成,做回调逻辑处理

})


异步回调的问题:

之前处理异步是通过纯粹的回调函数的形式进行处理

很容易进入到回调地狱中,剥夺了函数return的能力

问题可以解决,但是难以读懂,维护困难

稍有不慎就会踏入回调地狱 - 嵌套层次深,不好维护

need-to-insert-img

回调地狱

一般情况我们一次性调用API就可以完成请求。

有些情况需要多次调用服务器API,就会形成一个链式调用,比如为了完成一个功能,我们需要调用API1、API2、API3,依次按照顺序进行调用,这个时候就会出现回调地狱的问题

promise

promise是一个对象,对象和函数的区别就是对象可以保存状态,函数不可以(闭包除外)

并未剥夺函数return的能力,因此无需层层传递callback,进行回调获取数据

代码风格,容易理解,便于维护

多个异步等待合并便于解决

promise详解

new Promise(

  function (resolve, reject) {

    // 一段耗时的异步操作

    resolve('成功') // 数据处理完成

    // reject('失败') // 数据处理出错

  }

).then(

  (res) => {console.log(res)},  // 成功

  (err) => {console.log(err)} // 失败

)

resolve作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

reject作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

promise有三个状态:

1、当promise状态发生改变,就会触发then()里的响应函数处理后续步骤;

2、promise状态一经改变,不会再变。

3、Promise对象的状态改变,只有两种可能:

      从pending变为fulfilled

      从pending变为rejected。

这两种情况只要发生,状态就凝固了,不会再变了。

最简单示例:

new Promise(resolve => {

  setTimeout(() => {

    resolve('jianyan')

  }, 2000)

}).then(res => {

  console.log(res)

})

分两次,顺序执行

new Promise(resolve => {

    setTimeout(() => {

      resolve('jianyan')

    }, 2000)

  }).then(val => {

    console.log(val) //  参数val = 'hello'

    return new Promise(resolve => {

      setTimeout(() => {

        resolve('mochen')

      }, 2000)

    })

  }).then(val => {

    console.log(val) // 参数val = 'world'

  })

promise完成后then()

let pro = new Promise(resolve => {

  setTimeout(() => {

    resolve('hello world')

  }, 2000)

})

setTimeout(() => {

  pro.then(value => {

  console.log(value) // hello world

})

}, 2000)

结论:promise作为队列最为重要的特性,我们在任何一个地方生成了一个promise队列之后,我们可以把他作为一个变量传递到其他地方。

假如在.then()的函数里面不返回新的promise,会怎样?

.then()

1、接收两个函数作为参数,分别代表fulfilled(成功)和rejected(失败)

2、.then()返回一个新的Promise实例,所以它可以链式调用

3、当前面的Promise状态改变时,.then()根据其最终状态,选择特定的状态响应函数执行

4、状态响应函数可以返回新的promise,或其他值,不返回值也可以我们可以认为它返回了一个null;

5、如果返回新的promise,那么下一级.then()会在新的promise状态改变之后执行

6、如果返回其他任何值,则会立即执行下一级.then()

.then()里面有.then()的情况

1、因为.then()返回的还是Promise实例

2、会等里面的then()执行完,再执行外面的

need-to-insert-img

then嵌套

对于我们来说,此时最好将其展开,也是一样的结果,而且会更好读:

need-to-insert-img

展开增加可读性

错误处理

Promise会自动捕获内部异常,并交给rejected响应函数处理。

第一种错误处理

need-to-insert-img

第一种错误处理

第二种错误处理

need-to-insert-img

第二种错误处理

错误处理两种做法:

第一种:reject('错误信息').then(() => {}, () => {错误处理逻辑})

第二种:throw new Error('错误信息').catch( () => {错误处理逻辑})

推荐使用第二种方式,更加清晰好读,并且可以捕获前面所有的错误(可以捕获N个then回调错误)

catch() + then()

第一种情况:

need-to-insert-img

第一种情况

need-to-insert-img

第一种情况 - 结果

结论:catch也会返回一个promise实例,并且是resolved状态

第二种情况:

need-to-insert-img

第二种情况

need-to-insert-img

第二种情况结果

结论:抛出错误变为rejected状态,所以绕过两个then直接跑到最下面的catch

Promise.all() 批量执行

Promise.all([p1, p2, p3])用于将多个promise实例,包装成一个新的Promise实例,返回的实例就是普通的promise

它接收一个数组作为参数

数组里可以是Promise对象,也可以是别的值,只有Promise会等待状态改变

当所有的子Promise都完成,该Promise完成,返回值是全部值得数组

有任何一个失败,该Promise失败,返回值是第一个失败的子Promise结果

//切菜

    function cutUp(){

        console.log('开始切菜。');

        var p = new Promise(function(resolve, reject){        //做一些异步操作

            setTimeout(function(){

                console.log('切菜完毕!');

                resolve('切好的菜');

            }, 1000);

        });

        return p;

    }

    //烧水

    function boil(){

        console.log('开始烧水。');

        var p = new Promise(function(resolve, reject){        //做一些异步操作

            setTimeout(function(){

                console.log('烧水完毕!');

                resolve('烧好的水');

            }, 1000);

        });

        return p;

    }

    Promise.all([cutUp(), boil()])

        .then((result) => {

            console.log('准备工作完毕');

            console.log(result);

        })

Promise.race() 类似于Promise.all() ,区别在于它有任意一个完成就算完成

let p1 = new Promise(resolve => {

        setTimeout(() => {

            resolve('I\`m p1 ')

        }, 1000)

    });

    let p2 = new Promise(resolve => {

        setTimeout(() => {

            resolve('I\`m p2 ')

        }, 2000)

    });

    Promise.race([p1, p2])

        .then(value => {

            console.log(value)

        })

常见用法:

异步操作和定时器放在一起,,如果定时器先触发,就认为超时,告知用户;

例如我们要从远程的服务家在资源如果5000ms还没有加载过来我们就告知用户加载失败

现实中的用法

回调包装成Promise,他有两个显而易见的好处:

1、可读性好

2、返回 的结果可以加入任何Promise队列

实战示例,回调地狱和promise对比:

/***

  第一步:找到北京的id

  第二步:根据北京的id -> 找到北京公司的id

  第三步:根据北京公司的id -> 找到北京公司的详情

  目的:模拟链式调用、回调地狱

***/

// 回调地狱

// 请求第一个API: 地址在北京的公司的id

$.ajax({

  url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/city',

  success (resCity) {

    let findCityId = resCity.filter(item => {

      if (item.id == 'c1') {

        return item

      }

    })[0].id

    $.ajax({

      //  请求第二个API: 根据上一个返回的在北京公司的id “findCityId”,找到北京公司的第一家公司的id

      url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/position-list',

      success (resPosition) {

        let findPostionId = resPosition.filter(item => {

          if(item.cityId == findCityId) {

            return item

          }

        })[0].id

        // 请求第三个API: 根据上一个API的id(findPostionId)找到具体公司,然后返回公司详情

        $.ajax({

          url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/company',

          success (resCom) {

            let comInfo = resCom.filter(item => {

              if (findPostionId == item.id) {

                return item

              }

            })[0]

            console.log(comInfo)

          }

        })

      }

    })

  }

})

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

推荐阅读更多精彩内容