Creating a (ES6) promise without starting to resolve it

后端 未结 6 515
甜味超标
甜味超标 2020-12-07 17:15

Using ES6 promises, how do I create a promise without defining the logic for resolving it? Here\'s a basic example (some TypeScript):



        
相关标签:
6条回答
  • 2020-12-07 17:50

    Good question!

    The resolver passed to the promise constructor intentionally runs synchronous in order to support this use case:

    var deferreds = [];
    var p = new Promise(function(resolve, reject){
        deferreds.push({resolve: resolve, reject: reject});
    });
    

    Then, at some later point in time:

     deferreds[0].resolve("Hello"); // resolve the promise with "Hello"
    

    The reason the promise constructor is given is that:

    • Typically (but not always) resolution logic is bound to the creation.
    • The promise constructor is throw safe and converts exceptions to rejections.

    Sometimes it doesn't fit and for that it the resolver runs synchronously. Here is related reading on the topic.

    0 讨论(0)
  • 2020-12-07 17:51

    CPomise allows you to resolve your promises outside, but this is an antipattern since it breaks Promise incapsulation model. (Live demo)

    import CPromise from "c-promise2";
    
    const promise = new CPromise(() => {});
    
    promise.then((value) => console.log(`Done: ${value}`)); //123
    
    setTimeout(() => promise.resolve(123));
    
    0 讨论(0)
  • 2020-12-07 17:55

    Things are slowly getting better in JavaScript land, but this is one case where things are still unnecessarily complicated. Here's a simple helper to expose the resolve and reject functions:

    Promise.unwrapped = () => {
      let resolve, reject, promise = new Promise((_resolve, _reject) => {
        resolve = _resolve, reject = _reject
      })
      promise.resolve = resolve, promise.reject = reject
      return promise
    }
    
    // a contrived example
    
    let p = Promise.unwrapped()
    p.then(v => alert(v))
    p.resolve('test')
    

    Apparently there used to be a Promise.defer helper, but even that insisted on the deferred object being separate from the promise itself...

    0 讨论(0)
  • 2020-12-07 18:00

    What makes this kind of issues look complicated is the procedural nature of Javascript I guess. To resolve this, I created a simple class. Here how it looks:

    class PendingPromise {
    
        constructor(args) {
            this.args = args;
        }
    
        execute() {
            return new Promise(this.args);
        }
    }
    

    This promise will only be executed when you call execute(). For example:

    function log() {
        return new PendingPromise((res, rej) => {
            console.log("Hello, World!");
        });
    }
    
    log().execute();
    
    0 讨论(0)
  • 2020-12-07 18:07

    I want to add my 2 cents here. Considering exactly the question "Creating a es6 Promise without starting resolve it" I solved it creating a wrapper function and calling the wrapper function instead. Code:

    Let's say we have a function f which returns a Promise

    /** @return Promise<any> */
    function f(args) {
       return new Promise(....)
    }
    
    // calling f()
    f('hello', 42).then((response) => { ... })
    

    Now, I want to prepare a call to f('hello', 42) without actually solving it:

    const task = () => f('hello', 42) // not calling it actually
    
    // later
    task().then((response) => { ... })
    

    Hope this will help someone :)


    Referencing Promise.all() as asked in the comments (and answered by @Joe Frambach), if I want to prepare a call to f1('super') & f2('rainbow'), 2 functions that return promises

    const f1 = args => new Promise( ... )
    const f2 = args => new Promise( ... )
    
    const tasks = [
      () => f1('super'),
      () => f2('rainbow')
    ]
    
    // later
    Promise.all(tasks.map(t => t()))
      .then(resolvedValues => { ... })
    
    0 讨论(0)
  • 2020-12-07 18:07

    How about a more comprehensive approach?

    You could write a Constructor that returns a new Promise decorated with .resolve() and .reject() methods.

    You would probably choose to name the constructor Deferred - a term with a lot of precedence in [the history of] javascript promises.

    function Deferred(fn) {
        fn = fn || function(){};
    
        var resolve_, reject_;
    
        var promise = new Promise(function(resolve, reject) {
            resolve_ = resolve;
            reject_ = reject;
            fn(resolve, reject);
        });
    
        promise.resolve = function(val) {
            (val === undefined) ? resolve_() : resolve_(val);
            return promise;//for chainability
        }
        promise.reject = function(reason) {
            (reason === undefined) ? reject_() : reject_(reason);
            return promise;//for chainability
        }
        promise.promise = function() {
            return promise.then(); //to derive an undecorated promise (expensive but simple).
        }
    
        return promise;
    }
    

    By returning a decorated promsie rather than a plain object, all the promise's natural methods/properties remain available in addition to the decorations.

    Also, by handling fn, the revealer pattern remains availble, should you need/choose to use it on a Deferred.

    DEMO

    Now, with the Deferred() utility in place, your code is virtually identical to the jQuery example.

    var deferreds = {};
    function waitFor(key: string): Promise<any> {
      if (key in promises) {
        return deferreds[key].promise();
      }
      var def = Deferred();    
      deferreds[key] = def;
      return def.promise();
    }
    
    0 讨论(0)
提交回复
热议问题