首页 文章

在功能范围之外解决Javascript Promise

提问于
浏览
181

我一直在使用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();
}

这很好,但有更简单的方法吗?如果没有,这是一个好习惯吗?

9 回答

  • 80

    不,没有其他方法可以做到这一点 - 我唯一可以说的是这个用例并不常见 . 就像费利克斯在评论中所说的那样 - 你所做的将会持续发挥作用 .

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

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

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

    出于这个原因 - 每当你可以使用promise构造函数而不是导出函数时 - 我建议你使用它 . 每当你可以避免两者 - 避免两者和链 .

    请注意,对于像 if(condition) 这样的东西,你永远不应该使用promise构造函数,第一个例子可以写成:

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

    简单:

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

    这里的派对迟到了,但另一种方法是使用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
    })
    
  • 1

    我在2015年为我的框架提出的解决方案 . 我称这种承诺为 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
    
  • 12

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

    如果绑定 thencatch 然后绑定 Deferred 对象,那么它会完全实现 Promise API,您可以将其视为promise和 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();
    
  • 2

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

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

    用法是

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

    这与jQuery类似

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

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

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

    我们的解决方案是使用闭包来存储解析/拒绝函数,并附加一个函数来扩展promise本身 .

    这是模式:

    function getPromise() {
    
        var _resolve, _reject;
    
        var promise = new Promise((resolve, reject) => {
            _reject = reject;
            _resolve = resolve;
        });
    
        promise.resolve_ex = (value) => {
           _resolve(value);
        };
    
        promise.reject_ex = (value) => {
           _reject(value);
        };
    
        return promise;
    }
    

    并使用它:

    var promise = getPromise();
    
    promise.then(value => {
        console.info('The promise has been fulfilled: ' + value);
    });
    
    promise.resolve_ex('hello');  
    // or the reject version 
    //promise.reject_ex('goodbye');
    
  • 0

    我正在使用辅助函数来创建我称之为“扁平承诺”的东西 -

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

    查看完整的工作示例 -

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

    编辑:我创建了一个名为flat-promise的NPM包,代码也可用on GitHub .

  • 5

    我为此写了一个小lib . https://www.npmjs.com/package/@inf3rno/promise.exposed

    我使用了其他人编写的工厂方法方法,但我也覆盖了 thencatchfinally 方法,所以你也可以解决原来的承诺 .

    在没有来自外部的执行人的情况下解决承诺:

    const promise = Promise.exposed().then(console.log);
    promise.resolve("This should show up in the console.");
    

    从外部与执行者的setTimeout竞赛:

    const promise = Promise.exposed(function (resolve, reject){
        setTimeout(function (){
            resolve("I almost fell asleep.")
        }, 100000);
    }).then(console.log);
    
    setTimeout(function (){
        promise.resolve("I don't want to wait that much.");
    }, 100);
    

    如果您不想污染全局命名空间,则存在无冲突模式:

    const createExposedPromise = require("@inf3rno/promise.exposed/noConflict");
    const promise = createExposedPromise().then(console.log);
    promise.resolve("This should show up in the console.");
    

相关问题