入门Promise的正确姿式

Promise是异步编程的一种解决方案,从语法上说,Promise是一个对象,从它能够获取异步操做的消息。编程

Promise的基本用法

Promise构造函数接受一个函数做为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供。promise

  • resolve函数的做用是,将Promise对象的状态从“未完成”变为“成功”(即从Pending变为Resolved),在异步操做成功时调用,并将异步操做的结果做为参数传递出去。
  • reject函数的做用是,将Promise对象的状态从“未完成”变为“失败”(即从Pending变为Rejected),在异步操做失败时调用,并将异步操做报出的错误做为参数传递出去。
  • then方法能够接受两个回调函数做为参数。第一个回调函数是Promise对象的状态变为Resolved时调用,第二个回调函数是Promise对象的状态变为 Reject时调用
var promise = new Promise(
    //异步执行,Promise对象建立后会被当即执行
    function (resolve,reject) {
        //耗时很长的异步操做
  if('异步处理成功') {  
        resolve();    //数据处理成功时调用
  } else {
        reject();    //数据处理失败时调用
    }
        
    }
)
//Promise实例生成之后,能够用then方法分别指定Resolved状态和Reject状态的回调函数。
promise.then(
    function A() {
        //数据处理成功后执行
    },
    function B() {
        //数据处理失败后执行
    }
)

 

下面咱们举一个简单的例子来模拟一下异步操做成功和异步操做失败函数的运行过程。异步

 

console.log('starting');
var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("2秒后,我运行了");
        resolve('异步操做成功了');     //1
        //reject('异步操做失败了');    //2
     //return 'hello';       //3
}, 2000) }).then(function (value) { console.log('异步操做成功后执行我:',value); }, function (value) { console.log('异步操做失败后执行我:',value); } ) console.log('我也运行了'); // 上面的代码中1处代码的调用,输出顺序是: //starting //我也运行了
//2秒后,我运行了 // 异步操做成功后执行我: 异步操做成功了 // 上面的代码中2处代码的调用,输出顺序是: //starting //我也运行了
//2秒后,我运行了 // 异步操做失败后后执行我: 异步操做失败了


//上面的代码中3处代码的调用,输出顺序是:
//starting
//我也运行了
//2秒后,我运行了

知代码3处的return 'hello' 语句在新建的new Promise对象中并无被看成参数返回给then()函数内.那么会不会返回给promise了呢?咱们用一段代码来测试一下

console.log('starting');
var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("2秒后,我运行了");
        resolve('异步操做成功了');     //1
        //reject('异步操做失败了');    //2
        return 'hello';
    }, 2000) 
    
})
promise.then(function (value) { 
    console.log('异步操做成功后执行我:',value);
},
function (value) {
    console.log('异步操做失败后执行我:',value);
}
)
console.log('我也运行了');
console.log(promise);
setTimeout(function () {
    console.log('5秒后,我执行了');
    console.log(promise);
},5000);


//starting
//我也运行了
//Promise { pending }
  //[[PromiseStatus]]:"pending"
  //[[PromiseValue]]:undefined
  //__proto__:Promise {constructor: , then: , catch: , …}
//2秒后,我运行了
//异步操做成功后执行我: 异步操做成功了
//5秒后,我执行了
//Promise { resolved }
  //[[PromiseStatus]]:"resolved"
  //[[PromiseValue]]:"异步操做成功了"
  //__proto__:Promise {constructor: , then: , catch: , …}

由执行结果可知,变量promise仍然是new Promise对象的一个实例。因此return语句虽然被执行了,但对promise实例不会产生任何影响,至关于不存在。异步编程

由上面测试的代码可知,Promise对象有如下两个特色。
  (1)对象的状态不受外界影响。Promise对象表明一个异步操做,有三种状态:Pending(进行中)、Resolved(已完成,又称Fulfilled) 和Rejected(已失败)。只有异步操做的结果,能够决定当前是哪种状态,函数

  (2)一旦状态改变,就不会再变,任什么时候候均可以获得这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变 为Rejected。只要这两种状况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会当即获得这个结果。这与事件(Event)彻底不一样,事件的特色是,若是你错过了它,再去监听,是得不到结果的。测试

resolve(value) VS resolve(Promise)

咱们会在异步操做成功时调用resolve函数,其做用是将Promise对象的状态从Pending变为Resolved,并将异步操做的结果做为参数传递给then()方法里的第一个函数的形参spa

那么传入的参数是值和传入的参数是promise对象时有什么不一样呢。咱们来看一下例子。prototype

当传入的参数为值时:code

var time = new Date();
var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("1秒后,我运行了");
        resolve('异步操做成功了');     //1
    }, 2000) 
    
}).then(function (value) {
    console.log(value,new Date() - time);
})
//执行的输出结果为:
//2秒后,我运行了
//异步操做成功了 1002

大约过了一秒左右,咱们能够看到在resolved状态的回调方法中,咱们打印出了上面注释中的内容。咱们可以经过resolve方法传递操做的结果,而后在回调方法中使用这些结果。对象

若是咱们在resolve中传入一个Promise实例呢?

var time = new Date();
var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("2秒后,我运行了");
        resolve('异步操做成功了');     //1
    }, 2000) 
    
})

var promise2 = new Promise(function (resolve,reject) {
    setTimeout(resolve,1000,promise);//setTimeout和setInterval中的第三个、及之后的参数会做为第一个参数func的参数传递给func函数
}).then(function (value) {
    console.log(value,new Date() - time);
})

//执行后输出的结果为:
//
2秒后,我运行了 //异步操做成功了 2003

promise2通过了2秒后才打印出来结果。奇怪了,咱们不是设置promise2通过1秒后执行吗?

简单说就是由于promise2中的resolve()函数传入了promise对象,此时promise对象的状态决定了promise的状态,同时会把返回值传给promise。

Promise/A+中规定 [[Resolve]](promise, x)

2.3.2.若是x是一个promise实例, 则以x的状态做为promise的状态

  2.3.2.1.若是x的状态为pending, 那么promise的状态也为pending, 直到x的状态变化而变化。

  2.3.2.2.若是x的状态为fulfilled, promise的状态也为fulfilled, 而且以x的不可变值做为promise的不可变值。

  2.3.2.3.若是x的状态为rejected, promise的状态也为rejected, 而且以x的不可变缘由做为promise的不可变缘由。

2.3.4.若是x不是对象或函数,则将promise状态转换为fulfilled而且以x做为promise的不可变值。

 Promise.prototype.then()

Promise实例具备then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的做用是为Promise实例添加状态改变时的回调函数。 前面说过,then方法的第一个参数是Resolved状态的回调函数,第二个参数(可选)是Rejected状态的回调函数。

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

var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("2秒后,我运行了");
        resolve('异步操做成功了');     //1
    }, 2000) 
    
})
promise.name = 'promise';
console.log('promise:',promise)
var promise2 = promise.then(function (value) {
    console.log(value);
})
promise2.name = 'promise2';
console.log('promise2:',promise2);

// promise:
//     Promise { pending }
//     [[PromiseStatus]]:"pending"
//     [[PromiseValue]]:undefined
//     name:"promise"
//     __proto__:Promise {constructor: , then: , catch: , …}
// promise2:
// Promise { pending }
//     [[PromiseStatus]]:"pending"
//     [[PromiseValue]]:undefined
//     name:"promise2"
//     __proto__:Promise {constructor: , then: , catch: , …}

 

咱们能够知道promise.then()方法执行后返回的是一个新的Promise对象。也就是说上面代码中的promise2是一个Promise对象,它的实现效果和下面的代码是同样的,只不过在then()方法里,JS引擎已经自动帮咱们作了。

 

promise2 = new Promise(function (resolve,reject) {})

 

既然在then()函数里已经自动帮我实现了一个promise对象,可是我要怎么才能给resolve()或reject()函数传参呢?其实在then()函数里,咱们能够用return()的方式来给promise2的resolve()或reject()传参。看一个例子。

//var time = new Date();
var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("2秒后,我运行了");
        resolve('异步操做成功了');     //1
        //reject('异步操做失败了');    //2
    }, 2000) 
    
})
//console.log('promise:',promise)
var promise2 = promise.then(function (value) {
    console.log(value);
    //resolve('nihao');  //报错。注意,这里不能写resolve()方法,由于在then函数里是没有resolve方法的
    return (1);       
    //return promise;   //也能够return一个promise对象,返回promise对象执行后resolve('参数值')或reject('参数值')内部的参数值
  //若是不写return的话,默认返回的是return undefined 。
}) var promise3 = promise2.then(function (value) { console.log('is:',value); },function (value) { console.log('error:',value); }) promise2.name = 'promise2'; setTimeout(function () { console.log(promise2); },3000); //2秒后,我运行了 //异步操做成功了 //is: 1 //Promise {resolved} //name:"promise2" //__proto__:Promise //[[PromiseStatus]]:"resolved" //[[PromiseValue]]:1

Promise与错误状态处理

.then(null, rejection),用于指定异步操做发生错误时执行的回调函数。下面咱们作一个示例。

var promise = new Promise(function(resolve, reject) {
    setTimeout(function () {
            reject('error');
    },2000);
}).then(null,function(error) {
    console.log('rejected', error)
});
//rejected error

咱们知道then()方法执行后返回的也是一个promise对象,所以也能够调用then()方法,但这样的话为了捕获异常信息,咱们就须要为每个then()方法绑定一个.then(null, rejection)。因为Promise对象的错误信息具备“冒泡”性质,错误会一直向后传递,直到被捕获为止(向后传递指的是:后面的then函数会一直执行rejected下的代码,并抛出第一个then函数/Promise执行出错时的信息)。所以Promise为咱们提供了一个原型上的函数Promise.prototype.catch()来让咱们更方便的捕获到异常。

咱们看一个例子

var promise = new Promise(function(resolve, reject) {
    setTimeout(function () {
            reject('error');
    },2000);
}).then(function(value) {
    console.log('resolve', value);
}).catch(function (error) {
    console.log(error);
})
//运行结果
//error

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

可是若是用.then(null, rejection)方法来处理错误信息,咱们须要在每个rejection()方法中返回上一次异常信息的状态,这样当调用的then()方法一多的时候,对会对代码的清晰性和逻辑性形成影响。

因此,通常来讲,不要在then方法里面定义Reject状态的回调函数(即then的第二个参数),老是使用catch方法。

相关文章
相关标签/搜索