ES6 -- 异步编程神器:Promise对象

来源:互联网 发布:快手红人淘宝店铺大全 编辑:程序博客网 时间:2024/05/21 04:17

Promise是一种异步编程的解决方案,比起传统的回调函数方式,Promise要更合理和强大。


基本调用方式

下面直接给出代码,通过代码对Promise对象进行讲解和分析:

构造一个Promise实例的模版代码:

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

首先,Promise在ES6中是一个构造函数,用来生成Promise实例。所以,当我们需要Promise对象的时候,直接用new Promise(…)这样的方式即可。

我们调用new Promise方法的时候,它接受一个函数作为参数,而这个函数的两个参数分别是resolve, reject(我们现在并不用关心这个函数以及它的参数是从何而来的,我们首先只要按照这样的格式书写代码并会用就可以了),resolve和reject这两个参数其实也都是系统提供的函数,我们可以在这个函数中(也就是如上代码中// … some code的后面)的适当时机(比如:异步调用成功/失败后)可以直接调用它们。

调用后,Promise对象就会自动的触发then方法,如果我们调用了resolve方法,表示异步调用成功,那么就会执行作为then方法第一个参数的函数;如果我们调用了reject方法,表示异步调用失败,那么就会执行作为then方法第二个参数的函数。

补充一点:Promise对象有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败)。调用resolve方法可以使Promise对象的状态从“未完成”变为“成功”(即从Pending变为Resolved),而reject则将Promise对象的状态从“未完成”变为“失败”(即从Pending变为Rejected)。这两个函数都可以传递参数value或error,将可以将异步操作的结果传递出去,我们可以在then方法的参数(其实也是一个函数)中取到这个value或error。

另外,Promise新建后就会立即执行。

再看一个具体的例子,图片的异步获取:

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

这个栗子只写了第一段代码模版的第一部分,在这段代码中,我们就能清楚的看到何时以及如何调用resolve方法和reject方法:在图片加载成功的回调函数中调用了resolve,在图片加载失败的回调函数中调用了reject。


resolve参数的另一种可能

在前文中我们已经知道了:如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。

reject函数的参数通常是Error对象的实例,表示抛出的错误,这样,我们在回调函数中就可以接受并处理这个错误;resolve函数的参数除了正常的值以外,还可能是另一个Promise实例,表示异步操作的结果有可能是另一个异步操作。

例如:

var p1 = new Promise(function (resolve, reject) {  // ...});var p2 = new Promise(function (resolve, reject) {  // ...  resolve(p1);})

此时,p1的状态就会决定p2的状态——如果p1的状态是Pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是Resolved或者Rejected,那么p2的回调函数将会立刻执行。

栗子:

var p1 = new Promise(function (resolve, reject) {  setTimeout(() => reject(new Error('fail')), 3000)})var p2 = new Promise(function (resolve, reject) {  setTimeout(() => resolve(p1), 1000)})p2  .then(result => console.log(result))  .catch(error => console.log(error))// Error: fail

上面代码中,Promise实例p1在3秒之后变为rejected。而p2的状态在1秒之后改变,但是resolve方法返回的是p1。由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以,后面的then语句都变成针对后者(p1)。又过了2秒,p1变为rejected,才能导致触发catch方法指定的回调函数。


关于then方法的链式调用

then方法定义在Promise.prototype上,也就是说,是Promise实例的方法。因此,如果then方法中返回一个新的Promise实例,那么,then方法就可以被链式的调用,事实上,也正是如此;但是需要注意,then方法中返回的Promise已经不是调用then的那个Promise对象了,它是一个全新的promise对象。

例如像这样:
注:then方法中,第二个参数(对错误的处理函数)可以省略。

getJSON("/posts.json").then(function(json) {  return json.message;}).then(function(message) {  // ...});

由于getJSON返回的是一个Promise对象,那么它后面的then函数就会等待这个Promise对象的状态发生变化后被执行。而在这个then函数中,返回的json.message并不是一个异步操作,那么,它下面的一个then(第二个then)就会直接被调用。而如果,在这个then函数中返回的是一个promise对象,那么第二个then函数就会等到这个promise完成后(状态变为resolve或者reject)执行。

在下面这个栗子中,几个then函数会接连被调用,依次显示hello, world, !。

function printHello (ready) {    return new Promise(function (resolve, reject) {        if (ready) {            resolve("Hello");        } else {            reject("Good bye!");        }    });}function printWorld () {    alert("World");}function printExclamation () {    alert("!");}printHello(true)    .then(function(message){        alert(message);    })    .then(printWorld)    .then(printExclamation);

catch方法

Promise.prototype.catch方法是.then(null, rejection)的别名,(也就是then方法调用时省略第一个参数),它用于指定发生错误时的回调函数。

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

在上面这个栗子中,假设p1是一个已经定义过的promise对象,当p1中异步操作执行完毕,如果p1状态变为resolve,就会触发第一个then方法;如果p1状态变为reject,则会触发后面的catch方法(因为promise的错误具有冒泡的性质,会一直向后传递,直到被捕获)。同时,如果p1的then方法出现了错误,也会被catch函数捕获。

同时,由于catch函数实际上是then(null,rejection)的别名,所以在catch后,还可以继续链式调用then和catch函数。

由于Promise的错误具有冒泡的性质,于是建议大家总是使用catch,而不要在then方法中定义reject时的回调函数:

// 不推荐这种写法,这样在then的第一个函数参数中出现的错误无法被捕获promise1  .then(function(data) {    // success  }, function(err) {    // error  });// 推荐的方法,这种情况下,错误一定能被捕获promise2  .then(function(data) {     // success  })  .catch(function(err) {    // error  });

另外,在非chrome浏览器中,promise对象中出现的错误不会被传递到外层代码。

0 0
原创粉丝点击