• 约定:(1)JS事件队列的当前事件运行完成之前,回调函数不会被调用。 (2)通过.then形式添加的回调函数,甚至都在异步操作完成之后才添加的函数,都会被调用。 (3)通过多次.then形式添加回调函数,它们会按照插入顺序并且独自完成。
    • Promise最大的好处就是链式调用。
    • 连续执行两个或者多个异步操作的时候,每一个后来的操作都在前一个操作执行完成之后,带着前一个操作执行的结果继续执行。创建 Promise chain可以解决这种需求。 then 函数会返回一个新的 Promise,跟原来的不同:
      1. const promise = doSomething();
      2. const promise2 = promise.then(successCallback, failureCallback);

    或者

    1. const promise2 = doSomething().then(successCallback, failureCallback);

    第二个对象(promise2)不仅代表 DoSomething()函数的完成,也代表了你传入的 SuccessCallback或者 FailureCallback 的完成,这也可能是其他异步函数返回的 Promise。这样的话,任何被添加给 Promise2 的回调函数都会被排在 SuccessCallback或者 FailureCallback 返回的 Promise 后面。

    基本上,每一个 Promise 代表了链式中另一个异步过程的完成。

    在过去,做多重的异步操作,会导致经典的回调地狱:

    1. doSomething(function(result) {
    2. doSomethingElse(result, function(newResult) {
    3. doThirdThing(newResult, function(finalResult) {
    4. console.log('Got the final result: ' + finalResult);
    5. }, failureCallback);
    6. }, failureCallback);
    7. }, failureCallback);

    通过新式函数,我们把回调绑定到被返回的 Promise 上代替以往的做法,形成一个 Promise 链:

    1. doSomething().then(function(result) {
    2. return doSomethingElse(result);
    3. })
    4. .then(function(newResult) {
    5. return doThirdThing(newResult);
    6. })
    7. .then(function(finalResult) {
    8. console.log('Got the final result: ' + finalResult);
    9. })
    10. .catch(failureCallback);

    then里的参数是可选的,Catch(FailureCallback)是Then(null, FailureCallback)的缩略形式。

    也可以使用 Arrow Function (箭头函数 =>)来表示。(使用 () => x() => { return x; } 更简洁一点).

    • Catch的后续链式操作。在一个失败操作即一个 Catch操作完成之后可以继续操作链式操作,即使一个链式操作失败还能有助于新的动作继续完成。:

      1. new Promise((resolve, reject) => {
      2. console.log('Initial');
      3. resolve();
      4. })
      5. .then(() => {
      6. throw new Error('Something failed');
      7. console.log('Do this');
      8. })
      9. .catch(() => {
      10. console.log('Do that');
      11. })
      12. .then(() => {
      13. console.log('Do this whatever happened before');
      14. });

    输出结果如下:

    1. Initial
    2. Do that
    3. Do this whatever happened before

    注意,由于“Something failed”错误导致了拒绝操作,所以“Do this”文本没有被输出。

    • 一般,一个Promise链式遇到异常就会停止,查看链式的低端,寻找 Catch执行程序来代替当前执行。
    • 在旧式回调 API中创建Promise。理想状态下,所有的回调函数都会被调用,但是有些使用旧式回调 A的执行错误或者成功的情况下会可能产生混用旧式回调。混用旧式回调和Promise是会有问题的,我们可以把 A包装在最低级别,最好不要直接调用它们。

      1. const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
      2. wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback);

      通常,Promise的构造器会有一个让我们手动操作 Resolve和 Reject的执行函数。既然 Set timeOut没有真的执行失败,这时候可以忽略 Reject。

    • 调用(同步+异步),时序(同步),嵌套( Promise不使用嵌套,嵌套是因为粗心误用的)。

    • 常见错误

    在编写 Promise 链时,需要注意以下示例中展示的几个错误:

    1. // 错误示例,包含 3 个问题
    2. doSomething().then(function(result) {
    3. doSomethingElse(result) // 没有返回 Promise 以及没有必要的嵌套 Promise
    4. .then(newResult => doThirdThing(newResult));
    5. }).then(() => doFourthThing());
    6. // 最后是没有使用 catch 终止 Promise 调用链,可能导致没有捕获的异常

    第一个错误是没有正确的将事物相连接。当我们创建新 Promise 但忘记返回它时,会发生这种情况。因此,链条被打破,或者更确切地说,我们有两个独立的链条竞争(同时在执行两个异步而非一个一个的执行)。这意味 DoFourthThing()着 不会等待 DoSomethingElse()或者 DoThirdThing() 完成,并且将与它们并行运行,可能是无意的。单独的链也有单独的错误处理,导致未捕获的错误。
    第二个错误是不必要地嵌套,实现第一个错误。嵌套还限制了内部错误处理程序的范围,如果是非预期的,可能会导致未捕获的错误。其中一个变体是 Promise构造函数反模式 ,它结合了 Promise 构造函数的多余使用和嵌套。
    第三个错误是忘记用 Catch终止链。这导致在大多数浏览器中不能终止的 Promise 链里的 rejection。
    一个好的经验法则是总是返回或终止 Promise 链,并且一旦你得到一个新的 Promise,返回它。下面是修改后的平面化的代码:

    1. doSomething()
    2. .then(function(result) {
    3. return doSomethingElse(result);
    4. })
    5. .then(newResult => doThirdThing(newResult))
    6. .then(() => doFourthThing());
    7. .catch(error => console.log(error));

    现在我们有一个具有适当错误处理的确定性链。
    使用 Async/Await 解决了大多数,如果不是所有这些问题的话 - 最常见的错误就是忘记了 Await 关键字。