Promise的使用

一、Promise的含义

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。

二、Promise的特点

Promise对象有以下两个特点。

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

三、Promise的缺点

Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

四、基本用法

下面代码创造了一个Promise实例。

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

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

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

下面是一个Promise对象的简单例子。

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, 'done');
  });
}

timeout(100).then((value) => {
  console.log(value);
});

上面代码中,timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果。过了指定的时间(ms参数)以后,Promise实例的状态变为resolved,就会触发then方法绑定的回调函数。
Promise 新建后就会立即执行。

let promise = new Promise(function(resolve, reject) {
  console.log('Promise');
  resolve();
});

promise.then(function() {
  console.log('resolved.');
});

console.log('Hi!');
// Promise
// Hi!
// resolved

上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。

五、.then()

Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。(建议总是使用catch()方法,而不使用then()方法的第二个参数)

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

<script>
        function say(str, time) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve(str);
                }, time)
            })
        }
        say('你好', 1000).then(data => {
            console.log(data);
            return say('11', 1000)
        }).then(data => {
            console.log(data);
            return say('222', 1000)
        }).then(data => {
            console.log(data);
        })
    </script>

六、.catch()

用于指定发生错误时的回调函数。

const promise = new Promise(function(resolve, reject) {
  throw new Error('test');
});
promise.catch(function(error) {
  console.log(error);
});
// Error: test

上面代码中,promise抛出一个错误,就被catch()方法指定的回调函数捕获。注意,上面的写法与下面两种写法是等价的。
如果 Promise 状态已经变成resolved,再抛出错误是无效的。

const promise = new Promise(function(resolve, reject) {
  resolve('ok');
  throw new Error('test');
});
promise
  .then(function(value) { console.log(value) })
  .catch(function(error) { console.log(error) });
// ok

上面代码中,Promise 在resolve语句后面,再抛出错误,不会被捕获,等于没有抛出。因为 Promise 的状态一旦改变,就永久保持该状态,不会再变了。

Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

getJSON('/post/1.json').then(function(post) {
  return getJSON(post.commentURL);
}).then(function(comments) {
  // some code
}).catch(function(error) {
  // 处理前面三个Promise产生的错误
});

上面代码中,一共有三个 Promise 对象:一个由getJSON()产生,两个由then()产生。它们之中任何一个抛出的错误,都会被最后一个catch()捕获。

一般来说,不要在then()方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。

七、.finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

上面代码中,不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。

八、.all()

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.all([p1, p2, p3]);

上面代码中,Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。

p的状态由p1、p2、p3决定,分成两种情况。

(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

九、.race()

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

<script>
        var arr = [
            'https://res.vmallres.com/pimages//pages/picImages/60271072769517017206.jpg',
            'https://res.vmallres.com/pimages//pages/picImages/60694206179510249606.jpg',
            'https://res.vmallres.com/pimages//pages/picImages/55999684279518699955.jpg'
        ];

        function Imags(url) {
            return new Promise((resolve, reject) => {
                var img = document.createElement('img');
                img.src = url;
                img.onload = function() {
                    resolve(img)
                }
            })
        };
        // Promise.race([
        //     Imags(arr[0]),
        //     Imags(arr[1]),
        //     Imags(arr[2]),
        // ])
        Promise.race(arr.map(item => Imags(item))).then((data) => {
            document.body.append(data);
        }).catch(err => {
            console.log(err);
        })
    </script>

十、案例

 <script>
        $(function() {
            var urlA = 'https://apis.map.qq.com/ws/location/v1/ip?key=CAABZ-AVSAQ-RDR5L-GTBDJ-HLA4O-A5FDB&output=jsonp&_=1593478015329';
            var urlB = 'https://wis.qq.com/weather/common?weather_type=observe|forecast_24h|air&source=pc';
            var num = 0;

            function getUrl(url) {
                $.ajax({
                    url: url,
                    dataType: 'jsonp',
                }).then(data => {
                    num++
                    console.log(data);
                    if (num == 1) {
                        var info = data.result.ad_info;
                        $('.add').text(info.city)
                        return getUrl(urlB + `&province=${info.province}&city=${info.city}`)
                    }
                    if (num == 2) {
                        $('.weather').text(data.data.forecast_24h[0].day_weather);
                        $('.temp').text(data.data.forecast_24h[0].night_weather);
                        $('img').attr('src', `//mat1.gtimg.com/pingjs/ext2020/weather/pc/icon/weather/day/${ data.data.forecast_24h[0].day_weather_code}.png`)
                    }
                })
            }
            getUrl(urlA)
        })
    </script>
  • 22
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 22
    评论
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值