- 约定:(1)JS事件队列的当前事件运行完成之前,回调函数不会被调用。 (2)通过.then形式添加的回调函数,甚至都在异步操作完成之后才添加的函数,都会被调用。 (3)通过多次.then形式添加回调函数,它们会按照插入顺序并且独自完成。
- Promise最大的好处就是链式调用。
- 连续执行两个或者多个异步操作的时候,每一个后来的操作都在前一个操作执行完成之后,带着前一个操作执行的结果继续执行。创建 Promise chain可以解决这种需求。 then 函数会返回一个新的 Promise,跟原来的不同:
const promise = doSomething();const promise2 = promise.then(successCallback, failureCallback);
或者
const promise2 = doSomething().then(successCallback, failureCallback);
第二个对象(promise2)不仅代表 DoSomething()函数的完成,也代表了你传入的 SuccessCallback或者 FailureCallback 的完成,这也可能是其他异步函数返回的 Promise。这样的话,任何被添加给 Promise2 的回调函数都会被排在 SuccessCallback或者 FailureCallback 返回的 Promise 后面。
基本上,每一个 Promise 代表了链式中另一个异步过程的完成。
在过去,做多重的异步操作,会导致经典的回调地狱:
doSomething(function(result) {doSomethingElse(result, function(newResult) {doThirdThing(newResult, function(finalResult) {console.log('Got the final result: ' + finalResult);}, failureCallback);}, failureCallback);}, failureCallback);
通过新式函数,我们把回调绑定到被返回的 Promise 上代替以往的做法,形成一个 Promise 链:
doSomething().then(function(result) {return doSomethingElse(result);}).then(function(newResult) {return doThirdThing(newResult);}).then(function(finalResult) {console.log('Got the final result: ' + finalResult);}).catch(failureCallback);
then里的参数是可选的,Catch(FailureCallback)是Then(null, FailureCallback)的缩略形式。
也可以使用 Arrow Function (箭头函数 =>)来表示。(使用 () => x 比() => { return x; } 更简洁一点).
Catch的后续链式操作。在一个失败操作即一个 Catch操作完成之后可以继续操作链式操作,即使一个链式操作失败还能有助于新的动作继续完成。:
new Promise((resolve, reject) => {console.log('Initial');resolve();}).then(() => {throw new Error('Something failed');console.log('Do this');}).catch(() => {console.log('Do that');}).then(() => {console.log('Do this whatever happened before');});
输出结果如下:
InitialDo thatDo this whatever happened before
注意,由于“Something failed”错误导致了拒绝操作,所以“Do this”文本没有被输出。
- 一般,一个Promise链式遇到异常就会停止,查看链式的低端,寻找 Catch执行程序来代替当前执行。
在旧式回调 API中创建Promise。理想状态下,所有的回调函数都会被调用,但是有些使用旧式回调 A的执行错误或者成功的情况下会可能产生混用旧式回调。混用旧式回调和Promise是会有问题的,我们可以把 A包装在最低级别,最好不要直接调用它们。
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback);
通常,Promise的构造器会有一个让我们手动操作 Resolve和 Reject的执行函数。既然 Set timeOut没有真的执行失败,这时候可以忽略 Reject。
调用(同步+异步),时序(同步),嵌套( Promise不使用嵌套,嵌套是因为粗心误用的)。
- 常见错误
在编写 Promise 链时,需要注意以下示例中展示的几个错误:
// 错误示例,包含 3 个问题doSomething().then(function(result) {doSomethingElse(result) // 没有返回 Promise 以及没有必要的嵌套 Promise.then(newResult => doThirdThing(newResult));}).then(() => doFourthThing());// 最后是没有使用 catch 终止 Promise 调用链,可能导致没有捕获的异常
第一个错误是没有正确的将事物相连接。当我们创建新 Promise 但忘记返回它时,会发生这种情况。因此,链条被打破,或者更确切地说,我们有两个独立的链条竞争(同时在执行两个异步而非一个一个的执行)。这意味 DoFourthThing()着 不会等待 DoSomethingElse()或者 DoThirdThing() 完成,并且将与它们并行运行,可能是无意的。单独的链也有单独的错误处理,导致未捕获的错误。
第二个错误是不必要地嵌套,实现第一个错误。嵌套还限制了内部错误处理程序的范围,如果是非预期的,可能会导致未捕获的错误。其中一个变体是 Promise构造函数反模式 ,它结合了 Promise 构造函数的多余使用和嵌套。
第三个错误是忘记用 Catch终止链。这导致在大多数浏览器中不能终止的 Promise 链里的 rejection。
一个好的经验法则是总是返回或终止 Promise 链,并且一旦你得到一个新的 Promise,返回它。下面是修改后的平面化的代码:
doSomething().then(function(result) {return doSomethingElse(result);}).then(newResult => doThirdThing(newResult)).then(() => doFourthThing());.catch(error => console.log(error));
现在我们有一个具有适当错误处理的确定性链。
使用 Async/Await 解决了大多数,如果不是所有这些问题的话 - 最常见的错误就是忘记了 Await 关键字。
