ES6 Promise

来源:互联网 发布:淘宝客与卖家怎么分成 编辑:程序博客网 时间:2024/05/09 21:03

ES6 Promise

一 Promise的含义

Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和强大。它由社区最早提出和实现,ES6将它写进了语言标准(原生提供),统一了用法。
所谓Promise,简单来说是一个容器,里面保存着某个未来才会结束的事件的结果。从语法上看,Promise是一个对象,它可以获取异步操作的消息。Promise提供统一的API,各种异步操作都可以用同样的方法进行处理。

Promise对象有如下两个特点:

  • 1)对象的状态不受外界影响。

Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已成功,又称为Fulfilled)和Rejected(已失败)。
只有异步操作的结果,才可以决定当前是哪一种状态,任何其他操作都无法改变这一状态。这也是Promise名字的来由,即”承诺”,表示其他手段均无法改变。

  • 2)一旦到达已完成(已成功和已失败)状态,状态将不再发生改变,且可以随时获取该状态的结果。

Promise对象状态的改变,只有两种可能:从Pending->Resolved状态和从Pending->Rejected状态。只要发生了这两种状态的改变,状态就凝固了,不会在改变,会一直保持这个结果。
这种状态发生改变后,再对Promise对象添加回调函数,也会立即得到该状态下的结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,就得不到结果了。

有了Promise对象,就可以将异步操作以同步操作的方式表达出来,避免了过多的回调嵌套。此外,Promise对象提供了统一的接口,使得异步操作更加容易控制。

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

二 基本用法

在ES6中,Promise是一个构造函数,用来生成Promise实例。

var promise = new Promise(function(resolve,reject){    // some code    if(/* 异步操作成功 */){        resolve(value);    }else{        reject(error);    }});

Promise构造函数接收一个函数作为参数(回调函数),该函数又会接收两个函数参数,即上面代码的resolve和reject。
resolve函数是在异步操作成功时调用(即resolved状态下),并将异步操作的结果作为参数传递给resolve函数。reject函数是在异步操作失败时调用(即rejected状态下),并将异步操作的错误信息作为参数传递给reject函数。
Promise实例生成以后,也可以用then方法来分别指定Resolved状态和Rejected状态的回调函数。

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

then方法接收了两个回调函数作为参数,其中,前一个回调函数是Resolved状态下调用的,后一个回调函数是Rejected状态下被调用。第二个函数是可选的,就是说可以不传。这两个函数都可以接收Promise对象传出的的值作为参数。

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

function timeOut(ms){    return new Promise((resolve,reject) => {        setTimeout(resolve,ms,'done');    });}timeOut(100).then(value => {    console.log(value);});

注:上面”=>”表达式表示箭头函数,这也是ES6中的一个新增的特性。

上面代码中,timeOut返回一个Promise实例,表示一段时间以后会发生的结果。

一般情况下,Promise新建后就会立即执行。下面是一个用Promise异步加载图片的例子:

function loadImageAsync(url){    return new Promise(function(resolve,reject){        var image = new Image();        image.onload = function(){            resolve(image);        };        image.onerror new function(){            reject(new Error('Could not load image at'+url));        };        image.src = url;    });}

下面是一个Promise对象实现的ajax操作的例子:

var getJSON = function(url){    var promise= new Promise(function(resolve,reject){        var client=new XMLHttpRequest();        client.open("GET",url);        client.onreadystatechange = handler;        client.responseType = "json";        client.setRequestHeader("Accept","application/json");        client.send();        function handler(){            if(this.readyState != 4) return;            if(this.state===200){resolve(this.response);}            else {reject(new Error(this.statusText));}        };    });    return promise;};getJSON("/post.json").then(function(json){    console.log("Contents: "+json);},function(error){    console.log('出错了!',error);});

三 Promise.protype.then()

Promise的then方法是定义在原型对象Promise.protype上,它的作用是为Promise实例添加状态改变时的回调函数。具体用法上一节中有了介绍。
then方法返回的是一个新的Promise实例(注:不是原来的那个Promise),因此可以采用链式写法。

getJSON("/post.json").then(function(json){    return json.post;}).then(function(post){    // some code.});

上面链式写法中,第一个回调函数完成后,会将返回结果作为参数,传入第二个回调函数。

采用链式写法的then函数,可以指定一组按照次序调用的回调函数。有可能,前一个回调函数返回的还是一个Promise对象(即有异步操作),那么后一个回调函数就会等待该Promise对象的状态发生变化,才会被调用。

getJSON("/post/1.json").then(  post => getJSON(post.commentURL)).then(  comments => console.log("Resolved: ", comments),  err => console.log("Rejected: ", err));

四 Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。

getJSON("/posts.json").then(function(posts) {  // some code}).catch(function(error) {  // 处理 getJSON 和 前一个回调函数运行时发生的错误  console.log('发生错误!', error);});

上面代码中,getJSON方法返回一个Promise对象,如果该对象状态变为resolved,则调用then方法指定的回调函数;如果该对象状态变为rejected,则调用catch方法中指定的回调函数;另外,then方法中指定的回调函数,运行中抛出错误,也会被catch方法捕获。
这种写法比在then中设置rejected回调状态要好,原因是能捕获resolved状态回调函数中的错误,也更加接近同步的写法。

五 Promise.all()

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

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

上面的代码中,Promise.all方法接受一个数组作为参数,p1,p2,p3都是Promise对象的实例(如果不是,会自动先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理)。
p的状态由p1,p2,p3决定,分成两种情况:
1)当三者状态均变成resolved时,p的状态才会变成resolved。此时p1,p2,p3的返回值组成一个数组传递给p的回调函数;
2)当p1,p2,p3其中之一的状态为rejected时,p的状态就会变成rejected。此时,第一个被rejected的实例的返回值,会传递给p的回调函数。

六 Promise.race()

Promise.race方法也是讲多个Promise实例,包装成一个新的Promise实例。

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

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

七 Promise.resolve()

有时需要将现有对象转为Promise对象,Promise.resolve方法就起到这个作用。

var jsPromise = Promise.resolve($.ajax('../whatever.json'));

上面代码将jQuery生成的deferred对象,转为一个新的Promise对象。

Promise.resolve方法其实等价于下面的写法:

Promise.resolve('foo');// 等价于new Promise(resolve => resolve('foo'));

Promise.resolve方法的参数分为四种情况:

  • 1)参数是一个Promise实例

Promise.resolve将不做任何修改,原封不动地返回这个实例。

  • 2)参数是一个thenable对象

thenable对象指的是具有then方法的对象。
Promise.resolve方法会将这个对象转为Promise对象,然后就立即执行thenable对象的then方法。

  • 3)参数不是具有then方法的对象,或这根本就不是对象

如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的Promise对象,状态为Resolved。

  • 4)不带任何参数

Promise.resolve方法允许调用时不带参数,直接返回一个Resolved状态的Promise对象。
所以,如果希望得到一个Promise对象,比较方便的方法就是直接调用Promise.resolve方法。

八 Promise.reject()

Promise.reject(reason)方法也会返回一个新的Promise实例,该实例的状态为rejected。它的参数用法与Promise.resolve方法完全一致。

九 两个有用的附加方法

done()方法和finally()方法也是两个比较有用的方法。

0 0
原创粉丝点击