[书籍翻译] 《JavaScript并发编程》第三章 使用Promises实现同步 (5)

正如这个例子所表明的那样,我们不必在executor函数内处理它们。事实上,我们甚至不需要在创建和设置执行程序和完成函数之后显式引用promise实例。解析器函数已存储在某处,它包含对promise的引用。

类Promise对象

Promise类是一种原生的JavaScript类型。但是,我们并不总是需要创建新的promise实例来实现相同的同步操作。我们可以使用静态Promise.resolve()方法来解析这些对象。让我们看看如何使用此方法:

//“Promise.resolve()”方法可以处理thenable对象。 //这是一个带有“then()”方法的类似于执行器的对象。 //这个执行器将随机完成或拒绝promise。 Promise.resolve({then: (resolve, reject) => { Math.round(Math.random()) ? resolve('fulfilled') : reject('rejected'); //这个方法返回一个promise,所以我们能够 //设置已完成和被拒绝的回调函数。 }}).then((value) => { console.log('resolved', value); }, (reason) => { console.error('reason', reason); });

我们将在本章的最后一节中再次讨论Promise.resolve()方法,以了解更多用例。

建立回调链

我们在本章前面介绍的每种promise方法都会返回promise。这允许我们在返回值上再次调用这些方法,从而产生then().then()调用的链,依此类推。链式promise具有挑战性的一个方面是promise方法返回的是新实例。也就是说,我们将在本节中探讨promise在一定程度上的不变性。

随着我们的应用程序变得越来越大,并发性挑战随之增加。这意味着我们需要考虑更好的方法来利用原生同步语义,例如promises。正如JavaScript中的任何其他原始值一样,我们可以将它们从函数传递给函数。我们必须以同样的方式处理promises - 传递它们,并建立在回调函数链上。

Promises只改变状态一次

Promise初始时是等待状态,并且它们结束于已完成或被拒绝的状态。一旦promise转变为其中一种状态,它们就会锁定在这种状态。这有两个有趣的副作用。

首先,多次尝试完成或拒绝promise将被忽略。换句话说,解析器和拒绝器是幂等的 - 只有第一次调用对promise有影响。让我们看看这代码如何执行:

//此执行器函数尝试解析promise两次, //但完成的回调只调用一次。 new Promise((resolve, reject) => { resolve('fulfilled'); resolve('fulfilled'); }).then((value) => { console.log('then', value); }); //这个执行器函数尝试拒绝promise两次, //但拒绝的回调只调用一次。 new Promise((resolve, reject) => { reject('rejected'); reject('rejected'); }).catch((reason) => { console.error('reason'); });

promises仅改变状态一次的另一个含义是promise可以在添加完成或拒绝回调之前处理。竞争条件,例如这个,是并发编程的残酷现实。通常,回调函数会在创建时添加到promise中。由于JavaScript是运行到完成的,因此在添加回调之前,不会处理promise解析回调的任务队列。但是,如果promise立即在执行中解析怎么办?如果将回调添加到另一个JavaScript执行上下文的promise中会怎样?让我们看看是否可以用一些代码来更好地说明这些情况:

//此执行器函数立即解析promise。添加“then()”回调时, //promise已经解析了。但回调函数仍然会使用已解析的值进行调用。 new Promise((resolve, reject) => { resolve('done'); console.log('executor', 'resolved'); }).then((value) => { console.log('then', value); }); //创建一个立即解析的新promise执行器函数。 var promise = new Promise((resolve, reject) => { resolve('done'); console.log('executor', 'resolved'); }); //这个回调是promise解析后就立即执行了。 promise.then((value) => { console.log('then 1', value); }); //此回调在promise解析后未添加到另一个的promise中, //它仍然被立即调用并获得已解析的值。 setTimeout(() => { promise.then((value) => { console.log('then 2', value); }); }, 1000);

此代码说明了promises的一个非常重要的特性。无论何时将执行回调添加到promise中,无论是处于暂时挂起状态还是解析状态,使用promise的代码都不会更改。从表面上看,这似乎不是什么大不了的事。但是这种竞争条件检查的类型需要更多的并发代码来保护自己。相反,Promise原生语法为我们处理这个问题,我们可以开始将异步值视为原始类型。

不可改变的promises

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyjgpw.html