Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于Promise:你可能不知道的6件事 #1

Open
dwqs opened this issue Jul 30, 2016 · 11 comments
Open

关于Promise:你可能不知道的6件事 #1

dwqs opened this issue Jul 30, 2016 · 11 comments

Comments

@dwqs
Copy link
Owner

dwqs commented Jul 30, 2016

Promise 是一个非常简单的概念,即使你没有机会使用 Promise,你也可能阅读过一些关于 Promise 的文章。
Promise 的价值在于使得异步代码以一个更可读的风格结构化,而不是因异步函数嵌套显得混乱不堪。这篇文章会接触到 6 个你可能不知道的关于 Promise 的事。

开始列举之前,先看看怎么创建 Promise:

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

p.then(function(str) {
  alert(str);
});

1、then() 返回一个 forked Promise(分叉的 Promise)

下面两段代码有什么不同?

// Exhibit A
var p = new Promise(/*...*/);
p.then(func1);
p.then(func2);
// Exhibit B
var p = new Promise(/*...*/);
p.then(func1)
.then(func2);

如果你认为两段代码等价,那么你可能认为 promise 仅仅就是一维回调函数的数组。然而,这两段代码并不等价。p 每次调用 then() 都会返回一个 forked promise。因此,在A中,如果 func1 抛出一个异常,func2 依然能执行,而在B中,func2 不会被执行,因为第一次调用返回了一个新的 promise,由于 func1 中抛出异常,这个 promise 被 rejected了,结果 func2 被跳过不执行了。

2、回调函数应该传递结果

下面的代码会 alert 什么?

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

p.then(function(str) {})
.then(function(str) {
  alert(str);
});

第二个 then() 中的alert不是显示任何东西,因为在 promise 的上下文中,回调函数像普通的回调函数一样传递结果。promise 期望你的回调函数或者返回同一个结果,或者返回其它结果,返回的结果会被传给下一个回调。

这和适配器传递结果的思想一样,看下面的示例:

var feetToMetres = function(ft) { return ft*12*0.0254 };

var p = new Promise(/*...*/);

p.then(feetToMetres)
.then(function(metres) {
  alert(metres);
});

3、只能捕获来自上一级的异常

下面的两段代码有什么不同:

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

在A中,当第一个 then 抛出异常时,第二个 then 能捕获到该异常,并会弹出 'uh oh'。这符合只捕获来自上一级异常的规则。

在B中,正确的回调函数和错误的回调函数在同一级,也就是说,尽管在回调中抛出了异常,但是这个异常不会被捕获。事实上,B中的错误回调只有在 promise 被 rejected 或者 promise 自身抛出一个异常时才会被执行。

4、错误能被恢复

在一个错误回调中,如果没有重新抛出错误,promise 会认为你已经恢复了该错误,promise 的状态会转变为 resolved。在下面的例子中,会弹出’I am saved’ 是因为第一个 then() 中的错误回调函数并没有重新抛出异常。

var p = new Promise(function(resolve,reject){
    reject(new Error('pebkac'));
});  

p.then(
    undefined,
    function(error){ }
)
 .then(
    function(str){
        alert('I am saved!');
    },
    function(error){
     alert('Bad computer!');
    }
);   

Promise 可被视为洋葱的皮层,每一次调用 then 都会被添加一层皮层,每一个皮层表示一个能被处理的状态,在皮层被处理之后,promise 会认为已经修复了错误,并准备进入下一个皮层。

5、Promise 能被暂停

仅仅因为你已经在一个 then() 函数中执行过代码,并不意味着你不能够暂停 promise 去做其他事情。为了暂停当前的 promise,或者要它等待另一个 promise 完成,只需要简单地在 then() 函数中返回另一个 promise。

var p = new Promise(/*...*/);   

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

在上面的代码中,直到新的 promise 的状态是 resolved解析后,alert 才会显示。如果要在已经存在的异步代码中引入更多的依赖,这是一个很便利的方式。例如,你发现用户会话已经超时了,因此,你可能想要在继续执行后面的代码之前发起第二次登录。

6、resolved 状态的 Promise 不会立即执行

运行下面的代码会弹出什么呢?

function runme() {
  var i = 0;

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

你可能会认为弹出2,因为 promise 已经是 resolved ,then() 会立即执行(同步)。然而,promise 规范要求所有回调都是异步的,因此,alert 执行时 i 的值还没有被修改。

原文:Six Things You Might Not Know About Promises

荐读

Promise 的正确打开方式
Promise/A+
Promise 教程
JavaScript Promises 102 - The 4 Promise Methods

@FlowerWheel
Copy link

nice

@VectorHo
Copy link

VectorHo commented Aug 1, 2016

good

@rcholic
Copy link

rcholic commented Aug 2, 2016

读起来有点confusing,如果能在代码下面写上输出结果会更好一些

@dwqs
Copy link
Owner Author

dwqs commented Aug 3, 2016

@rcholic 恩 由于是译文 没考虑到这一点 自己写的文章会考虑的 多谢建议

@KangarooZero
Copy link

好棒!

@geyingauv
Copy link

看的有点困,刚开始研究js
blog不错 继续支持 :)

@xiaonuonuo
Copy link

5、Promise 能被暂停

var = new Promise(/.../);

p.then(function(str){
if(!loggedIn){
return new Promise(/.../);
}
})

var后面少了一个字面量

@dwqs
Copy link
Owner Author

dwqs commented Feb 26, 2018

@xiaonuonuo 多谢指出 已经修改

@plh97
Copy link

plh97 commented Apr 2, 2018

更想知道的是.then内部的代码被扔到哪去了。最初想的是,类似于setTimeout,被扔到下一个线程里面等待执行,但是google了一下,又觉得不是这样,看博主的文章,p.then(func1()) p.then(func2()),这两段互不影响。.then分岔了?

@Tiny-Fendy
Copy link

@pengliheng 这个很容易解释,去MDN里面看一下官方额文档就都清楚了。
then方法会返回另一个实例化的Promise。所以p.then(func1()),p.then(func2())会返回不同的Promise。

@random-yang
Copy link

引用代码:

var feetToMetres = function(ft) { return ft*12*0.0254 };

var p = new Promise(/*...*/); // 问题行

p.then(feetToMetres)
.then(function(metres) {
  alert(metres);
});

上面的代码“问题行”好像有问题?
不应该是:var p = Promise.resolve(/*...*/)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants