Promise 对象 -- 01

130 阅读5分钟
该文章是阅读阮一峰老师的ES6标准入门第十四章节后的总结以及自己的一些感受。
es6.ruanyifeng.com/#docs/promi…

1.1 Promise 的含义

Promise是异步编程的一种解决方案--回调函数和事件--更合理且更强大,所谓的Promise,简单来说其实就是一个容器,里面保存着某个未来时刻才会结束的是事件,通常是一个异步操作的结果。从语法上来说,Promise是一个对象,它可以获取异步操作的结果,Promise提供了统一的API,各种异步的操作都可以用同样的方法进行处理。

Promise有以下两个特点。

  1. 对象的状态不受外界的影响。promise代表一个异步的操作,有3种状态:Pending(进行中),Fulfilled(已成功),Rejected(已失败)。只有异步的操作结果可以决定当前的状态是哪一种,任何的其他操作都不可以改变这个状态。这也就是promise名称的由来,像一个承诺一样。
  2. 一旦状态改变就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变只有2种可能:从Pending -->Fulfiled 或者从Pending --> Rejected。只有这两种情况的发生,状态就凝固了,不会再改变了,而且会一直保持这个结果,这时候就称为Resolved(已定型),就算改变已经发生,再对Promise对象添加回调函数,也会立即得到这个结果。这个与事件不同,事件的特点是,如果错过了,再监听是得不到结果的。

Promise也是有一些缺点的。

  • 无法取消Promise,一旦建立它就会立即执行,无法再中途取消,如果不设置回掉函数,Promise抛出的错误不会反射到外部。再者,但出于Pending的时候,无法得知当前进行到哪一个阶段

1.2 基本用法

ES6规定,Promise对象是一个构造函数,用来生成Promise实例。

var promise = new Promise(function(resolve,reject){
    // 进行异步请求
    if(res.statusCode == 200){ //异步请求成功回调
        resolve(res)
    }else{
        reject(error)
    }
})

Promise的构造函数接受2个参数,分别是 resolve ,reject,这两个函数是由javascript提供的。resolve的作用是,将Promise状态由未完成 --> 成功,即:pending --> resolved ,在异步操作成功的时候调用,并将异步操作成功的结果作为参数传递出去;reject函数的作用是,将Promise的状态由未完成 --> 失败,即:pending --> rejected,在异步操作失败的时候调用,并将操作失败报出的错误传递出去。

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

promsie.then(function(value){
    //成功回调返回的值
    console.log(value)
},function(error){
    //失败回调返回的错误信息
    console.log(error)
})

其中,then的第二个失败的回调函数可以不写。

Demo - 1

function timeout(ms){
    return new Promise((resolve,reject)=>{
        setTimeout(resolve,ms)
    })
}
timeout(100).then((val) => {
    console.log(val)
})

Demo -2  Promise建立后就会立即执行。

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

promise.then((val) => {
    console.log('promise has done')
})

console.log('hi')

//promsie
//hi
//promise has done

上述的代码中,Promise建立后就会执行,所以最先输出promise,然后,then的方法指定的回调函数将在当前脚本所有的同步任务完成之后才会执行,所以resolve的结果之后输出。

Demo -3 使用Promise实现AJAX操作。

var myPromiseAjax = function(url,type){
    retunr new Promise((resolve,reject) => {
        var client = new XMLHttpRequest();
        client.open(type,url);
        client.onreadystatechange = handler;
        client.responseType = 'json';
        client.setRequestHeader("Accept","application/json");
        client.send();

        function handler(){
            if(this.readyState !== 4){
                retunn;
            }
            if(this.status == 200){
                resolve(res)
            }else{
                reject(new Error(this.statusText))
            }
        }
    })
}

myPromiseAjax("test.php",POST).then((res) => {
    console.log(res)
},(error) => {console.log(error)})

上述代码中,如果resolve和reject都带了参数,那么这些参数将会被传递给这些回调函数,reject函数的参数通常是Error对象的实例,表示抛出的异常,resolve函数除了正常的异步操作返回的值之外,还与可能是一个Promise实例,比如Demo -4。

Demo -4

var p1 = new Promise((resolve,reject)=>{});

var p2 = new Promise((resolve,reject)=>{
    resolve(p1)
})

上述的代码中,p1 p2都是Promise的实例,但是P2的resolve将p1作为参数,即一个异步操作的结果是另外一个异步操作,此时的p1状态就会传递给p2,也就是说,p1的状态决定了P2的状态。如果P1是pending,那么P2就会等待P1的结果,直到P1的状态变为resolved或者rejected,P2才会立即执行。

var p1 = new Promise(() => {
    setTimeout(() => reject(new Error('fail')),3000)
})

var p2 = new Promise(() => {
    setTimeout(() => resolve(p1),1000)
})

p2.then(result => console.log(resilt)).catch(error => console.log(error));

上述代码中,P1是一个Promise,3秒之后变为rejected。P2的状态在1秒之后改变,resolve方法返回的是P1,由于P2返回的是一个Promise,导致P2的状态无效,由P1的状态决定了P2的状态,所以,then语句都变成针对后者P1的,在过2秒,P1变为rejected触发了catch方法指定的回调函数。

注意:调用resolve和reject并不会终结Promise参数函数的执行。

new Promise((resolve,reject) => {
    resolve(1);
    console.log(2)
}).then(r => {
    console.log(r)
})

上述代码中,掉用resolve(1);之后,console还是会执行,并且会先执行,这是因为resolved的Promise是在本轮事件循环的末尾进行,总是晚于本循环的同步任务。

一般来说,调用resolve或reject以后,Promise的任务就完成了,后面的继续操作应该放到then里面执行,而不是写到resolve之后,所以,最好在他们的后面加上 return 语句,这样就不会产生意外。