最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to avoid wrapping code in a promise callback? - Stack Overflow

programmeradmin1浏览0评论

In order not to wrap blocks of code in a promise callback (to save one level of indentation), I sometime do the following:

function myFunction() {
    // Create the promise object and get the "resolve" callback
    let promiseResolve = null;
    const promise = new Promise((resolve, reject) => {
        promiseResolve = resolve;
    });

    // Then later in the code:
    setTimeout(() => {
        // Something slow
        promiseResolve();
    }, 1000);

    // And at the end
    return promise
}

It works but it feels a bit messy. Is there any proper pattern to do this in JavaScript?

In order not to wrap blocks of code in a promise callback (to save one level of indentation), I sometime do the following:

function myFunction() {
    // Create the promise object and get the "resolve" callback
    let promiseResolve = null;
    const promise = new Promise((resolve, reject) => {
        promiseResolve = resolve;
    });

    // Then later in the code:
    setTimeout(() => {
        // Something slow
        promiseResolve();
    }, 1000);

    // And at the end
    return promise
}

It works but it feels a bit messy. Is there any proper pattern to do this in JavaScript?

Share Improve this question asked Nov 28, 2017 at 0:30 laurentlaurent 91k83 gold badges310 silver badges442 bronze badges 3
  • 2 Yeah, it's messy. There just no reason to avoid putting the code that does the resolve inside the promise executor callback. That's where it belongs. – jfriend00 Commented Nov 28, 2017 at 0:36
  • If you want to save a level of indentation, and assuming you don't need a closure, you can always factor out the promise body into a separate function. – Amadan Commented Nov 28, 2017 at 0:57
  • If you want to save your level of indentation, don't do plex logic in setTimeout callbacks where exceptions aren't caught. Instead use return new Promise(resolve => setTimeout(resolve, 1000)).then(() => { /* something slow */ }); – Bergi Commented Nov 28, 2017 at 3:04
Add a ment  | 

1 Answer 1

Reset to default 10

Is there any proper pattern to do this in JavaScript?

Yes, the proper pattern is what you are apparently trying to avoid.

function myFunction(data) {
    return new Promise((resolve, reject) => {
        // put code in here that calls resolve() or reject()
        someAsyncOperation(data, function(err, result) {
            if (err) {
                reject(err);
            } else {
                resolve(result);
            }
        });
    });
}

For what you're showing, there's just really no reason to assign the resolve() or reject() handlers outside the scope of the executor function.

Here are some of the benefits to keeping your code inside the Promise executor:

  1. It is automatically throw-safe. If you throw synchronously inside the executor function (accidentally), it will automatically be caught and turned into a rejection for that promise.
  2. If you throw synchronously in your code outside the promise executor function, it will not be caught at all and your function will throw synchronously. It's very bad to return a promise and throw synchronously as that puts a lot of burden on the caller to watch for errors in two separately mechanisms.
  3. You don't make any case for why you should add plication to your code just to avoid putting some code at a level of indentation that you already have anyways.
  4. The promise executor was designed this way for a bunch of good reasons. What you are showing a preference for is essentially the "Deferred" model where you create an object you can pass around that has exposed methods on it for resolving or rejecting. That is purposely not the design center for promises and there was very good thinking on why it is not the way they decided to go. I will see if I can find some of those debates and discussions to reference, but I know I've seen them and they make sense to me.
  5. There are very, very, very occasionally some rationale cases for a Deferred-like interface where the code is significantly simpler using that model. You can make your own Deferred object using the promise executor (which is almost what you're trying to do here), but it really should only be done when there's a proven need for it, not just because you prefer that coding style or want to avoid putting code inside the executor callback.
  6. You have a lot of rep here so I'm guessing you're a fairly experienced guy so I'm surprised you're resisting putting the code inside the executor because Javascript encourages this a LOT. It's an extremely mon style (using anonymous callbacks and embedding code logic in those callbacks). Even promise .then() and .catch() require that. To program in Javascript, one has to be used to that and expect it. It is often required in order to access parent scoped variables too.
  7. In general, you don't want to mix callback and promise code in the same logic flow. So, when I want to use a promise interface with non-promise-based callback interfaces, the first thing I do is I make a promisified interface for the callback interfaces at the lowest level possible so I can then use that interface entirely with a promise interface. In most cases, I use an automated way to promisify the interface such as util.promisify() or Bluebird's Promise.promisifyAll(). Then, my logic flow is entirely made out of promise-based operations and I'm never even faced with the type of code you show and any temptation to write code like that is gone too.

Some references:

Why does the Promise constructor need an executor?

Deferred Anti-Pattern

The Revealing Constructor Pattern

What's the correct pattern for Promise.defer?


If you really find that you think you "need" a Deferred object, then I'd suggest you encapsulate that functionality in a new object and use that, rather than manually coding it each time you use it. In all my coding, I've only ever found that code was easier to write and cleaner with a Deferred object and that was a pretty unusual system of a low level queue managing a bunch of a different tasks.

There are several examples of simple, short pieces of code to implement a Deferred object. Here's one:

Why does the Promise constructor need an executor?

发布评论

评论列表(0)

  1. 暂无评论