promises并非真正不可改变。它们改变状态,then()方法将回调函数添加到promise。但是,有一些不可改变的promises特征值得在这里讨论,因为它们会在某些情况下影响我们的promise代码。
从技术上讲,then()方法实际上并没有改变promise对象。它创建了所谓的promise能力,它是一个引用promise的内部JavaScript记录,以及我们添加的函数。因此,它不是JavaScript语言中的真正语法。
这是一张图,说明当我们链接两个或更多then()一起调用时会发生什么:
我们可以看到,then()方法不会返回与上下文一起调用的相同实例。相反,then()创建一个新的promise实例并返回它。让我们看一些代码,来进一步的说明当我们使用then()将promises链接在一起时会发生的事情:
//创建一个立即解析的promise, //并且存储在“promise1”中。 var promise1 = new Promise((resolve, reject) => { resolve('fulfilled'); }); //使用“promise1”的“then()”方法创建一个 //新的promise实例,存储在“promise2”中。 var promise2 = promise1.then((value) => { console.log('then 1', value); //→then 1 fulfilled }); //为“promise2”创建一个“then()”回调。这实际上 //创建第三个promise实例,但我们不用它做任何事情。 promise2.then((value) => { console.log('then 2', value); //→then 2 undefined }); //确信“promise1”和“promise2”实际上是不同的对象 console.log('equal', promise1 === promise2); //→equal false我们可以清楚地看到这两个创建promise的实例在这个例子中是独立的promise对象。值得指出的是第二个promise执行前时,一定是它执行了第一个promise。但是,我们可以看到的是该值不会传递到第二个promise。我们将在下一节中解决此问题。
有多少个then()回调,就有多少个promise对象正如我们在上一节中看到的那样,使用then()创建的promise将绑定到它们的创建者。也就是说,当第一个promise完成时,绑定它的promise也会完成,依此类推。但是,我们也发现了一个小问题。已解析的值不会使其传递到第一个回调函数。这样做的原因是为响应promise解析而运行的每个回调都是第一个回调的返回值被送入第二个回调,依此类推。我们的第一个回调将值作为参数的原因是因为这在promise机制中显然会发生的。
我们来看看另一个promise链示例。这一次,我们将显式返回回调函数中的值:
//创建一个新promise随机调用解析回调或拒绝回调。 new Promise((resolve, reject) => { Math.round(Math.random()) ? resolve('fulfilled') : reject('rejected'); }).then((value) => { //在完成原始promise时调用返回值, //以防另一个promise链接到这一个。 console.log('then 1', value); return value; }).catch((reason) => { //链接到第二个promise, //当拒绝回调时执行。 console.error('catch 1', reason); }).then((value) => { //链接到第三个promise, //按预期得到值,并返回值给任何下个promise回调使用。 console.log('then 2', value); return value; }).catch((reason) => { //这里永不会被调用, //拒绝回调不会通过promise链传递。 console.error('catch 2', reason); });这看起来不错。我们可以看到已解析的值通过promise链传递。有一个异常 - 拒绝回调不会向后传递。相反,只有链中的第一个promise拒绝回调会执行。其余的promise回调只是完成,而不是拒绝。这意味着最后一个catch()回调永远不会运行。
当我们以这种方式将promise链接在一起时,我们的执行回调函数需要能够处理错误条件。例如,已解析的值可能具有error属性,可以检查其具体问题。
promises传递在本节中,我们讲讲promise作为原始值的用法。我们经常用原始值做的事情是将它们作为参数传递给函数,并从函数中返回它们。promise和其他原生语法之间的关键区别在于我们如何使用它们。其他值是始终都存在,而promise的值到未来某个时间点才存在。因此,我们需要通过回调函数定义一些操作过程,当值获得时去执行。
promises的好处是用于提供这些回调函数的接口小巧且一致。当我们将值与将作用于它的代码耦合时,我们不需要再去自主创造同步机制。这些单元可以像任何其他值一样在我们的应用程序中运用,并且并发语义是常见的。这是几个promise函数相互传递的示图: