舒彩光的互联网生涯
用心做事做人,做最认真的程序员

使用promise模式你应该知道的6点

2014年02月20日 00:05,by 舒彩光

Promise是一个很简单的概念,也许你还没有来得及使用,但你一定了解过这个概念。Promise是一个非常有用的结构,使异步代码更具可读性。而不会产生多层匿名函数的嵌套,使结构非常混乱。这篇文章将会介绍6个与Promise有关的注意事项。

在潜心研究之前,先展示一下如何使用javascript promise:

var p = new Promise(function(resolve, reject) {
  resolve("hello world");
});
 
p.then(function(str) {
  alert(str);
});

1. then() 返回新的promise

如下两段代码:

// 示例1
var p = new Promise(/*...*/);
p.then(func1);
p.then(func2);
// 示例2
var p = new Promise(/*...*/);
p.then(func1)
.then(func2);

如果你认为上面两段代码的含义是一样的,那么你对promise的认识只限于一维数组的回调方式。实际上每调用一次then()就会返回一个新的promise。的示例1中,如是func1抛出一个错误,func2仍会被正常调用。

在示例2中,如果func1抛出一个错误,func2将不会被调用。因为第一次调用then()时返回的新的promise由于func1的错误而没有被返回。所以func2将会被跳过。

提示:promise可以实现复杂的多分支结构。

2.在回调间传递结果

如下面的代码:

var p = new Promise(function(resolve, reject) {
  resolve("hello world");
});
 
p.then(function(str) {})
.then(function(str) {
  alert(str);
});

上面的代码第二个then()中的alert将不能弹出任何信息。因为在第一个then()中没有将结果传递到后面的then().promist模式预期每个回调函数都返回相同结果或者替代者,这个结果将会被传递给下一个回调函数。

这种想法类似于使用适配器来变换结果,如下面示例:

var feetToMetres = function(ft) { return ft*12*0.0254 };
 
var p = new Promise(/*...*/);
 
p.then(feetToMetres)
.then(function(metres) {
  alert(metres);
});

3.捕抓前级的异常。

比较一下下面两个代码有什么不同:

在示例A中,第一个then()中抛出一个异常,在第二个then()当中将会捕捉到这个异常,并会输出”哈哈”。

// 示例 A
new Promise(function(resolve, reject) {
  resolve("hello world");
})
.then(
  function(str) {
    throw new Error("哈哈");
  },
  undefined
)
.then(
  undefined,
  function(error) {
    alert(error);
  }
);
// 示例 B
new Promise(function(resolve, reject) {
  resolve("hello world");
})
.then(
  function(str) {
    throw new Error("哈哈");
  },
  function(error) {
    alert(error);
  }
);

在示例B中,异步回调函数和异常回调函数都在一起。这时当回调函数抛出异常时,异常回调不会捕捉到异常。实际上示例B中的异常回调函数只有在拒绝模式或promise本身抛出异常的情况下才会运行。

4.异常可以跳过

如果在一个异常回调函数中没有重新抛出异常,则promise认为异常被恢复返回到正常状态。下面的示例当中,将会弹“显示我,哈哈!”。因为在该示例中第一个then()中的异常回调没有重新抛出异常。

var p = new Promise(function(resolve, reject) {
  reject(new Error("pebkac"));
});
 
p.then(
  undefined,
  function(error) { }
)
.then(
  function(str) {
    alert("显示我,哈哈!");
  },
  function(error) {
    alert("不能显示我");
  }
);

5.promise可以暂停

如果你想暂停当前的promise继续执行下一个then(),或等待其他任务的完成,你就可以用这种办法,如下面的代码所示:

var p = new Promise(/*...*/);
 
p.then(function(str) {
  if(!loggedIn) {
    return new Promise(/*...*/);
  }
})
.then(function(str) {
  alert("完成");
})

在第一个then()当中return一个新的promise.这样只有直到这个新的promise启动后,才会执行第二个then().弹了“完成”对话框。如上代码所示,当检测到用户的登录session过期后,通过第二个promise弹出登录框让用户登录后再继续执行第二个then()中的动作。

6.promise不是立即执行的。

function runme() {
  var i = 0;
 
  new Promise(function(resolve) {
    resolve();
  })
  .then(function() {
    i += 2;
  });
  alert(i);
}

如上代码所示,你可能认为将会弹出2,但是promise是异步调用。所以代码不是顺序执行的。所以alert会弹出0;