解析函数范围外的 Javascript Promise

IT技术 javascript promise es6-promise
2021-02-08 16:55:01

我一直在使用 ES6 Promise。

通常,一个 Promise 是这样构造和使用的

new Promise(function(resolve, reject){
    if (someCondition){
        resolve();
    } else {
        reject();
    } 
});

但是为了灵活性,我一直在做类似下面的事情来解决问题。

var outsideResolve;
var outsideReject;
new Promise(function(resolve, reject) { 
    outsideResolve = resolve; 
    outsideReject = reject; 
});

然后

onClick = function(){
    outsideResolve();
}

这工作正常,但有没有更简单的方法来做到这一点?如果不是,这是一个好习惯吗?

6个回答

简单的:

var promiseResolve, promiseReject;

var promise = new Promise(function(resolve, reject){
  promiseResolve = resolve;
  promiseReject = reject;
});

promiseResolve();
问题中已经提到了这个确切的构造。你甚至读过吗?
2021-03-13 16:55:01
我什至不确定这是一个糟糕的设计。在Promise之外抛出的错误不应该在Promise内被捕获。如果设计者实际上希望错误被发现,这可能是一个误解或错误理解的例子
2021-03-14 16:55:01
@ruX,正如公认的答案所提到的 - 它是故意这样设计的。关键是如果抛出异常,它将被 promise 构造函数捕获。这个答案(以及我的)有一个陷阱,即可能为任何代码调用抛出异常promiseResolve()Promise的语义是它总是返回一个值。此外,这在功能上与 OP 的帖子相同,我不明白这是在以可重用的方式解决什么问题。
2021-03-27 16:55:01
@JonJaques 我不确定你说的是否属实。调用的代码promiseResolve()不会抛出异常。您可以.catch在构造函数上定义 a ,无论什么代码调用它,.catch都会调用构造函数这是 jsbin 演示这是如何工作的:jsbin.com/yicerewivo/edit?js,console
2021-03-31 16:55:01
是的,它被捕获是因为你在它周围包裹了另一个Promise构造函数 - 这正是我想要表达的观点。但是,假设您有一些其他代码试图在构造函数(又名延迟对象)之外调用 resolve() ......它可能会抛出异常而不会被捕获jsbin.com/cokiqiwapo/1/edit?js,console
2021-04-10 16:55:01

这里的聚会有点晚了,但另一种方法是使用Deferred对象。您基本上拥有相同数量的样板文件,但如果您想传递它们并可能在它们的定义之外解析,这很方便。

幼稚的实现:

class Deferred {
  constructor() {
    this.promise = new Promise((resolve, reject)=> {
      this.reject = reject
      this.resolve = resolve
    })
  }
}

function asyncAction() {
  var dfd = new Deferred()

  setTimeout(()=> {
    dfd.resolve(42)
  }, 500)

  return dfd.promise
}

asyncAction().then(result => {
  console.log(result) // 42
})

ES5 版本:

function Deferred() {
  var self = this;
  this.promise = new Promise(function(resolve, reject) {
    self.reject = reject
    self.resolve = resolve
  })
}

function asyncAction() {
  var dfd = new Deferred()

  setTimeout(function() {
    dfd.resolve(42)
  }, 500)

  return dfd.promise
}

asyncAction().then(function(result) {
  console.log(result) // 42
})
使用延迟是执行此操作的常用方法,我不知道为什么这不更高
2021-03-16 16:55:01
请注意这里的词汇范围。
2021-03-23 16:55:01
是按resolve|reject词法分配还是通过 分配没有实际区别bind这只是自 1.0(ish) 以来一直存在的jQuery Deferred对象的简单实现除了没有抛出安全之外,它的工作方式与 promise 完全一样。这个问题的重点是如何在创建Promise时节省几行代码。
2021-03-27 16:55:01
优秀的答案!正在寻找 jQuery 提供的延迟功能。
2021-04-03 16:55:01
Deferred弃用?
2021-04-11 16:55:01

不,没有其他方法可以做到这一点 - 我唯一能说的是这个用例不是很常见。就像 Felix 在评论中所说的那样 - 你所做的将始终有效。

值得一提的是,promise 构造函数以这种方式运行的原因是抛出安全——如果在你的代码在 promise 构造函数中运行时发生了你没有预料到的异常,它将变成拒绝,这种形式的抛出安全——将抛出的错误转换为拒绝很重要,有助于维护可预测的代码。

出于这个抛出安全的原因,promise 构造函数被选择而不是 deferreds(这是一种替代的promise构造方式,允许你正在做的事情)——至于最佳实践——我会传递元素并使用promise构造函数代替:

var p = new Promise(function(resolve, reject){
    this.onclick = resolve;
}.bind(this));

出于这个原因——无论何时你可以使用 promise 构造函数而不是导出函数——我建议你使用它。当你可以避免两者时 - 避免两者和连锁。

请注意,您永远不应该将 promise 构造函数用于诸如if(condition),第一个示例可以写为:

var p = Promise[(someCondition)?"resolve":"reject"]();
@Domi 检查 q-connection 和 RxJS。
2021-03-18 16:55:01
嗨,本杰明!如果我们不知道什么时候兑现Promise,那么目前有没有更好的方法来获得美味的Promise糖?像某种异步等待/通知模式例如,“存储”,然后调用Promise链?例如,在我的特定情况下,我在服务器上,等待特定的客户端回复(SYN-ACK 有点握手以确保客户端成功更新状态)。
2021-03-22 16:55:01
我如何使用 fetch API 做同样的事情?
2021-03-23 16:55:01
不常见?我最终几乎在每个项目中都需要它。
2021-03-30 16:55:01
至于用例,请考虑您需要在触发事件并发生其他事情后做某事。您想将事件转换为Promise并将其与另一个Promise联合起来。对我来说似乎是一个普遍的问题。
2021-04-07 16:55:01

我喜欢@JonJaques 的回答,但我想更进一步。

如果绑定thencatch随后的Deferred对象,那么它完全实现了PromiseAPI,你可以把它当作Promise,await它与这样的。

⚠️ 编者注:我不推荐这种模式了,因为在写这篇文章的时候,Promise.prototype.finally还不是一个东西,然后它变成了一个东西……这可能发生在其他方法上,所以我建议你用resolvereject函数来扩充promise实例反而:

function createDeferredPromise() {
  let resolve
  let reject

  const promise = new Promise((thisResolve, thisReject) => {
    resolve = thisResolve
    reject = thisReject
  })

  return Object.assign(promise, {resolve, reject})
}

去upvote别人的答案。

class DeferredPromise {
  constructor() {
    this._promise = new Promise((resolve, reject) => {
      // assign the resolve and reject functions to `this`
      // making them usable on the class instance
      this.resolve = resolve;
      this.reject = reject;
    });
    // bind `then` and `catch` to implement the same interface as Promise
    this.then = this._promise.then.bind(this._promise);
    this.catch = this._promise.catch.bind(this._promise);
    this.finally = this._promise.finally.bind(this._promise);
    this[Symbol.toStringTag] = 'Promise';
  }
}

const deferred = new DeferredPromise();
console.log('waiting 2 seconds...');
setTimeout(() => {
  deferred.resolve('whoa!');
}, 2000);

async function someAsyncFunction() {
  const value = await deferred;
  console.log(value);
}

someAsyncFunction();

我真的很喜欢这个。谢谢你。我在我的 Express 应用程序中将它用作自定义定义的组件,但如果您愿意创建一个 NPM module,它会很棒,或者如果需要我也可以。这种方法很好地融合了新的 async / await 以及旧的 Parse Platform 如何处理 promise en.wikipedia.org/wiki/Parse_(platform)
2021-03-18 16:55:01
不要忘记Promise.prototype.finally.
2021-03-28 16:55:01
好吧,如果您担心 的方法未来可能发生的变化Promise,您还可以通过循环遍历 的属性来概括映射工作Promise,不是吗?
2021-04-09 16:55:01
很好的收获@КонстантинВан,我一分钟都没有看到这个答案,我不再推荐这个了。我已经更新了答案以反映
2021-04-11 16:55:01

我在 2015 年为我的框架提出了一个解决方案。我把这种类型的 promise 称为Task

function createPromise(handler){
  var resolve, reject;

  var promise = new Promise(function(_resolve, _reject){
    resolve = _resolve; 
    reject = _reject;
    if(handler) handler(resolve, reject);
  })
  
  promise.resolve = resolve;
  promise.reject = reject;
  return promise;
}


// create
var promise = createPromise()
promise.then(function(data){ alert(data) })

// resolve from outside
promise.resolve(200)
谢谢,这有效。但什么是处理程序?我不得不将其删除才能使其正常工作。
2021-03-20 16:55:01
感谢您的代码!但是.resolve在回调设置之前,其他一些代码是否可以调用您的代码我习惯于常规线程,而不是异步事件,所以我可能有点困惑。
2021-04-03 16:55:01
@Sahid 当您运行 createPromise() 时,您需要将一个函数作为参数传递给它。否则代码不起作用。您可以使用 if 语句并在调用之前检查处理程序参数的有效性。
2021-04-08 16:55:01