我们可以看到,解析器函数被调用时fulfilled()函数会被调用。执行器实际上并不调用解析器。相反,它将解析器函数传递给另一个异步函数 - setTimeout()。执行器并不是我们试图去弄清楚的异步代码。可以将执行器视为一种协调程序,它编排异步操作并确定何时执行Promise。
前面的示例未解析任何值。当某个操作的调用者需要确认它成功或失败时,这是一个有效的用例。相反,让我们这次尝试解析一个值,如下所示:
//我们的Promise使用的执行函数。 //创建Promise后,设置延时一秒钟调用"resolve()", //并解析返回一个字符串值 - "done!"。 function executor(resolve) { setTimeout(() => { resolve('done!'); }, 1000); } //我们Promise的完成回调接受一个值参数。 //这个值将传递到解析器。 function fulfilled(value) { console.log('resolved', value); } //创建我们的Promise,提供执行程序和完成回调函数。 var promise = new Promise(executor); promise.then(fulfilled);我们可以看到这段代码与前面的例子非常相似。区别在于我们的解析器函数实际上是在传递给setTimeout()的回调函数的闭包内调用的。这是因为我们正在解析一个字符串值。还有一个将被解析的参数值传递给我们的fulfilled()函数。
拒绝promisesPromise执行器函数并不总是按期望进行,当出现问题时,我们需要拒绝promise。这是从等待状态转换到另一个可能的状态。这不是进入一个完成状态而是进入一个被拒绝的状态。这会导致执行不同的回调,与完成回调函数是分开的。值得庆幸的是,拒绝Promise的机制与完成Promise非常相似。我们来看看这是如何实现的:
//此执行器在延时一秒后拒绝Promise。 //它使用拒绝回调函数来改变状态, //并传递拒绝的参数值到回调函数。 function executor(resolve, reject) { setTimeout(() => { reject('Failed'); }, 1000); } //用作拒绝回调的函数。 //它接收提供拒绝的参数值。 function rejected(reason) { console.error(reason); } //创建promise,并运行执行器。 //使用“catch()”方法来接收拒绝回调函数。 var promise = new Promise(executor); promise.catch(rejected);这段代码看起来和在上一节中看到的代码非常相似。我们设置了超时,并且我们拒绝了它而不是完成它。这是使用rejector函数完成的,并作为第二个参数传递给执行器。
我们使用catch()方法而不是then()方法来设置拒绝回调函数。我们将在本章后面看到then()方法如何用于同时处理完成和拒绝回调函数。此示例中的拒绝回调函数仅将失败原因打印出来。通常情况下提供此返回值很重要。当我们完成promise时,返回值也是常见的,尽管不是必需的。另一方面,对于拒绝函数,一般也很少有情况仅仅通过回调函数输出拒绝原因。
让我们看下另一个例子,它捕获执行器中抛出的异常,并为拒绝回调函数提供更有意义的报错原因:
//此promise执行程序抛出错误, //并调用拒绝回调函数输出错误信息。 new Promise(() => { throw new Error('Problem executing promise'); }).catch((reason) => { console.error(reason); }); //此promise执行程序捕获错误, //并调用拒绝回调函数输出更有意义的错误信息。 new Promise((resolve, reject) => { try { var size = this.name.length; } catch (error) { reject(error instanceof TypeError ? 'Missing "name" property' : error); } }).catch((reason) => { console.error(reason); });前一个例子中第一个Promise的有趣之处在于它确实改变了状态,即使我们没有使用resolve()或reject()明确地改变promise的状态。然而,最终改变promise的状态是很重要的; 我们将在下一节中探讨这个话题。
空Promises尽管事实上执行器函数传递了一个完成回调函数和拒绝回调函数,但并不保证promise将改变状态。有些情况下,promise只是挂起,并没有触发完成回调也没有触发拒绝回调。这可能并没有什么问题,事实上,简单的promises,就很容易发现和修复没有响应的promises。然而,随着我们进入更复杂的场景后,一个promise的完成回调可以作为其他几个promise的回调结果。如果一个promises不能完成或拒绝,然后整个流程将崩溃。这种情况调试起来是非常麻烦的;下面的图可以很清楚的看到这个情况:
在图中,我们可以看到哪个promise导致依赖的promise挂起,但通过调试代码来解决这个问题并不容易。现在让我们看看导致promise挂起的执行函数:
//这个promise能够正常运行执行器函数。 //但“then()”回调函数永远不会被执行。 new Promise(() => { console.log('executing promise'); }).then(() => { console.log('never called'); }); //此时,我们并不知道promise出了什么问题 console.log('finished executing, promise hangs');