解析函数范围外的 Javascript Promise

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26150232/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-22 22:24:35  来源:igfitidea点击:

Resolve Javascript Promise outside function scope

javascriptpromisees6-promise

提问by Morio

I have been using ES6 Promise.

我一直在使用 ES6 Promise。

Ordinarily, a Promise is constructed and used like this

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

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

But I have been doing something like below to take the resolve outside for the sake of flexibility.

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

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

And later

然后

onClick = function(){
    outsideResolve();
}

This works fine, but is there an easier way to do this? If not, is this a good practice?

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

采纳答案by Benjamin Gruenbaum

No, there is no other way to do this - the only thing I can say is that this use case isn't very common. Like Felix said in the comment - what you do will consistently work.

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

It's worth mentioning that the reason the promise constructor behaves this way is throw safety - if an exception you did not anticipate happens while your code is running inside the promise constructor it will turn into a rejection, this form of throw safety - converting thrown errors to rejections is important and helps maintain predictable code.

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

For this throw safety reason, the promise constructor was chosen over deferreds (which are an alternative promise construction way that do allow what you're doing) - as for best practices - I'd pass the element and use the promise constructor instead:

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

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

For this reason - whenever you canuse the promise constructor over exporting the functions - I recommend you do use it. Whenever you can avoid both - avoid both and chain.

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

Note, that you should never use the promise constructor for things like if(condition), the first example could be written as:

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

var p = Promise[(someCondition)?"resolve":"reject"]();

回答by carter

simple:

简单的:

var promiseResolve, promiseReject;

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

promiseResolve();

回答by Jon Jaques

Bit late to the party here, but another way to do it would be to use a Deferredobject. You essentially have the same amount of boilerplate, but it's handy if you want to pass them around and possibly resolve outside of their definition.

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

Naive Implementation:

幼稚的实现:

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 Version:

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
})

回答by Maxmaxmaximus

A solution I came up with in 2015 for my framework. I called this type of promises Task

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

function createPromise(handler){
  var _resolve, _reject;

  var promise = new Promise(function(resolve, reject){
    _resolve = resolve; 
    _reject = reject;

    handler(resolve, reject);
  })

  promise.resolve = _resolve;
  promise.reject = _reject;

  return promise;
}

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

promise.resolve(200) // resolve from outside

回答by Rico Kahler

I liked @JonJaques answer but I wanted to take it a step further.

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

If you bind thenand catchthen the Deferredobject, then it fully implements the PromiseAPI and you can treat it as promise and awaitit and such.

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

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[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();

回答by Cory Danielson

A helper method would alleviate this extra overhead, and give you the same jQuery feel.

辅助方法可以减轻这种额外的开销,并为您提供相同的 jQuery 感觉。

function Deferred() {
    let resolve;
    let reject;
    const promise = new Promise((res, rej) => {
        resolve = res;
        reject = rej;
    });
    return { promise, resolve, reject };
}

Usage would be

用法是

const { promise, resolve, reject } = Deferred();
displayConfirmationDialog({
    confirm: resolve,
    cancel: reject
});
return promise;

Which is similar to jQuery

类似于 jQuery

const dfd = $.Deferred();
displayConfirmationDialog({
    confirm: dfd.resolve,
    cancel: dfd.reject
});
return dfd.promise();


Although, in a use case this simple, native syntax is fine

虽然在用例中这种简单的原生语法很好

return new Promise((resolve, reject) => {
    displayConfirmationDialog({
        confirm: resolve,
        cancel: reject
    });
});

回答by Arik

I'm using a helper function to create what I call a "flat promise" -

我正在使用一个辅助函数来创建我所谓的“平面承诺” -

function flatPromise() {

    let resolve, reject;

    const promise = new Promise((res, rej) => {
      resolve = res;
      reject = rej;
    });

    return { promise, resolve, reject };
}

And I'm using it like so -

我就是这样使用它的 -

function doSomethingAsync() {

    // Get your promise and callbacks
    const { resolve, reject, promise } = flatPromise();

    // Do something amazing...
    setTimeout(() => {
        resolve('done!');
    }, 500);

    // Pass your promise to the world
    return promise;

}

See full working example -

查看完整的工作示例 -

function flatPromise() {

    let resolve, reject;

    const promise = new Promise((res, rej) => {
        resolve = res;
        reject = rej;
    });

    return { promise, resolve, reject };
}

function doSomethingAsync() {
    
    // Get your promise and callbacks
    const { resolve, reject, promise } = flatPromise();

    // Do something amazing...
    setTimeout(() => {
        resolve('done!');
    }, 500);

    // Pass your promise to the world
    return promise;
}

(async function run() {

    const result = await doSomethingAsync()
        .catch(err => console.error('rejected with', err));
    console.log(result);

})();

Edit: I have created an NPM package called flat-promiseand the code is also available on GitHub.

编辑:我创建了一个名为flat-promise的 NPM 包,代码也可以在 GitHub 上找到

回答by Hinrich

You can wrap the Promise in a class.

您可以将 Promise 包装在一个类中。

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

        this.promise.resolve = this.resolve;
        this.promise.reject = this.reject;

        return this.promise;
    }
    promise;
    resolve;
    reject;
}

// How to use.
const promise = new Deferred((resolve, reject) => {
  // Use like normal Promise.
});

promise.resolve(); // Resolve from any context.

回答by Ali

Accepted answer is wrong. It's pretty easy using scope and references, though it may make Promise puristsangry:

接受的答案是错误的。使用范围和引用非常容易,尽管它可能会让 Promise纯粹主义者生气:

const createPromise = () => {
    let resolver;
    return [
        new Promise((resolve, reject) => {
            resolver = resolve;
        }),
        resolver,
    ];
};

const [ promise, resolver ] = createPromise();
promise.then(value => console.log(value));
setTimeout(() => resolver('foo'), 1000);

We are essentially grabbing the reference to the resolve function when the promise is created, and we return that so it can be set externally.

我们本质上是在创建 Promise 时获取对 resolve 函数的引用,然后我们返回它以便可以在外部设置它。

In one second the console will output:

一秒钟后,控制台将输出:

> foo

回答by jamess

Many of the answers here are similar to the last example in this article. I am caching multiple Promises, and the resolve()and reject()functions can be assigned to any variable or property. As a result I am able to make this code slightly more compact:

这里的许多答案都类似于在最后一个例子这篇文章。我正在缓存多个 Promise,并且可以将resolve()reject()函数分配给任何变量或属性。因此,我可以使这段代码稍微紧凑一些:

function defer(obj) {
    obj.promise = new Promise((resolve, reject) => {
        obj.resolve = resolve;
        obj.reject  = reject;
    });
}

Here is a simplified example of using this version of defer()to combine a FontFaceload Promise with another async process:

以下是使用此版本defer()FontFace负载 Promise 与另一个异步进程组合的简化示例:

function onDOMContentLoaded(evt) {
    let all = []; // array of Promises
    glob = {};    // global object used elsewhere
    defer(glob);
    all.push(glob.promise);
    // launch async process with callback = resolveGlob()

    const myFont = new FontFace("myFont", "url(myFont.woff2)");
    document.fonts.add(myFont);
    myFont.load();
    all.push[myFont];
    Promise.all(all).then(() => { runIt(); }, (v) => { alert(v); });
}
//...
function resolveGlob() {
    glob.resolve();
}
function runIt() {} // runs after all promises resolved 

Update: 2 alternatives in case you want to encapsulate the object:

更新:如果您想封装对象,有两种选择:

function defer(obj = {}) {
    obj.promise = new Promise((resolve, reject) => {
        obj.resolve = resolve;
        obj.reject  = reject;
    });
    return obj;
}
let deferred = defer();

and

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