在JavaScript中实现承诺

The thing I love most about programming is the aha! moment when you start to fully understand a concept. Even though it might take a long time and no small amount of effort to get there, it sure is worth it.

我最喜欢编程的是啊哈! 当您开始完全理解一个概念的那一刻。 即使可能要花很长时间并且要花很少的精力才能到达目的地,但确实值得。

I think that the most effective way to assess (and help improve) our degree of comprehension of a given subject is to try and apply the knowledge to the real world. Not only does this let us identify and ultimately address our weaknesses, but it can also shed some light on the way things work. A simple trial and error approach often reveals those details that had remained elusive previously.

我认为评估(并帮助提高)我们对特定主题理解程度的最有效方法是尝试将知识应用于现实世界。 这不仅使我们能够确定并最终解决我们的弱点,而且还可以阐明事情的工作方式。 一个简单的 法,常常发现了仍然难以实现先前那些细节。

With that in mind, I believe that learning how to implement promises was one of the most important moments in my programming journey — it has given me invaluable insight into how asynchronous code works and has made me a better programmer overall.

考虑到这一点,我认为学习如何实现Promise是我的编程旅程中最重要的时刻之一-它使我对异步代码的工作原理具有无价的了解,使我整体上成为一个更好的程序员。

I hope that this article will help you come to grips with implementing promises in JavaScript as well.

我希望本文也能帮助您掌握在JavaScript中实现诺言的方法。

We shall focus on how to implement the promise core according to the Promises/A+ specification with a few methods of the Bluebird API. We are also going to be using the TDD approach with Jest.

我们将重点介绍如何通过Promises / A +规范使用Bluebird API的几种方法来实现Promise核心。 我们还将在Jest中使用TDD方法

TypeScript is going to come in handy, too.

TypeScript将派上用场。

Given that we are going to be working on the skills of implementation here, I am going to assume you have some basic understanding of what promises are and and a vague sense of how they work. If you don’t, here is a great place to start.

鉴于我们将在此处研究实施技巧,因此我假设您对什么是诺言以及对诺言如何工作有一定的了解。 如果您不这样做,那么这里是个不错的起点。

Now that we have that out of the way, go ahead and clone the repository and let’s get started.

现在我们已经解决了这个问题,继续克隆存储库 ,让我们开始吧。

承诺的核心 (The core of a promise)

As you know, a promise is an object with the following properties:

如您所知,promise是具有以下属性的对象:

然后 (Then)

A method that attaches a handler to our promise. It returns a new promise with the value from the previous one mapped by one of the handler’s methods.

一种将处理程序附加到我们的诺言的方法。 它返回一个新的promise,该promise具有由处理程序的方法之一映射的前一个promise的值。

处理程序 (Handlers)

An array of handlers attached by then. A handler is an object containing two methods onSuccess and onFail, both of which are passed as arguments to then(onSuccess, onFail).

那时附加的处理程序数组。 处理程序是一个包含两个方法onSuccessonFail的对象,这两个方法均作为参数传递给then ( onSuccessonFail )

type HandlerOnSuccess<T, U = any> = (value: T) => U | Thenable<U>;
type HandlerOnFail<U = any> = (reason: any) => U | Thenable<U>;

interface Handler<T, U> {
  onSuccess: HandlerOnSuccess<T, U>;
  onFail: HandlerOnFail<U>;
}
(State)

A promise can be in one of three states: resolved, rejected, or pending.

一个承诺可以处于以下三种状态之一:已解决, 拒绝, 要么 待定

Resolved means that either everything went smoothly and we received our value, or we caught and handled the error.

解决就意味着一切顺利进行,我们获得了价值,或者我们发现并处理了错误。

Rejected means that either we rejected the promise, or an error was thrown and we didn’t catch it.

被拒绝意味着我们要么拒绝了承诺,要么抛出了一个错误而我们没有抓住它。

Pending means that neither the resolve nor the reject method has been called yet and we are still waiting for the value.

挂起表示尚未调用resolve方法或reject方法,并且我们仍在等待该值。

The term “the promise is settled” means that the promise is either resolved or rejected.

术语“诺言已实现”是指诺言被解决或被拒绝。

(Value)

A value that we have either resolved or rejected.

我们已解决或拒绝的值。

Once the value is set, there is no way of changing it.

一旦设置了值,就无法更改它。

测试中 (Testing)

According to the TDD approach, we want to write our tests before the actual code comes along, so let’s do just that.

根据TDD方法,我们希望在实际代码出现之前就编写测试,所以让我们开始吧。

Here are the tests for our core:

这是我们核心的测试:

describe('PQ <constructor>', () => {
  test('resolves like a promise', () => {
    return new PQ<number>((resolve) => {
      setTimeout(() => {
        resolve(1);
      }, 30);
    }).then((val) => {
      expect(val).toBe(1);
    });
  });

  test('is always asynchronous', () => {
    const p = new PQ((resolve) => resolve(5));

    expect((p as any).value).not.toBe(5);
  });

  test('resolves with the expected value', () => {
    return new PQ<number>((resolve) => resolve(30)).then((val) => {
      expect(val).toBe(30);
    });
  });

  test('resolves a thenable before calling then', () => {
    return new PQ<number>((resolve) =>
      resolve(new PQ((resolve) => resolve(30))),
    ).then((val) => expect(val).toBe(30));
  });

  test('catches errors (reject)', () => {
    const error = new Error('Hello there');

    return new PQ((resolve, reject) => {
      return reject(error);
    }).catch((err: Error) => {
      expect(err).toBe(error);
    });
  });

  test('catches errors (throw)', () => {
    const error = new Error('General Kenobi!');

    return new PQ(() => {
      throw error;
    }).catch((err) => {
      expect(err).toBe(error);
    });
  });

  test('is not mutable - then returns a new promise', () => {
    const start = new PQ<number>((resolve) => resolve(20));

    return PQ.all([
      start
        .then((val) => {
          expect(val).toBe(20);
          return 30;
        })
        .then((val) => expect(val).toBe(30)),
      start.then((val) => expect(val).toBe(20)),
    ]);
  });
});
运行我们的测试 (Running our tests)

I highly recommend using the Jest extension for Visual Studio Code. It runs our tests in the background for us and shows us the result right there between the lines of our code as green and red dots for passed and failed tests, respectively.

我强烈建议对Visual Studio Code使用Jest扩展 。 它在后台为我们运行测试,并向我们显示代码行之间的结果,分别为通过和失败测试的绿色和红色点。

To see the results, open the “Output” console and choose the “Jest” tab.

要查看结果,请打开“输出”控制台,然后选择“玩笑”选项卡。

We can also run our tests by executing the following command:

我们还可以通过执行以下命令来运行测试:

npm run test

Regardless of how we run the tests, we can see that all of them come back negative.

无论我们如何运行测试,我们都可以看到所有测试结果都呈阴性。

Let’s change that.

让我们改变它。

实施Promise核心 (Implementing the Promise core)

构造函数 (constructor)
class PQ<T> {
  private state: States = States.PENDING;
  private handlers: Handler<T, any>[] = [];
  private value: T | any;
  public static errors = errors;

  public constructor(callback: (resolve: Resolve<T>, reject: Reject) => void) {
    try {
      callback(this.resolve, this.reject);
    } catch (e) {
      this.reject(e);
    }
  }
}

Our constructor takes a callback as a parameter.

我们的构造函数将回调作为参数。

We call this callback with this.resolve and this.reject as arguments.

我们使用this.resolvethis.reject作为参数调用此回调。

Note that normally we would have bound this.resolve and this.reject to this, but here we have used the class arrow method instead.

注意,通常我们将this.resolvethis.reject绑定到this ,但是这里我们改用class arrow方法。

setResult (setResult)

Now we have to set the result. Please remember that we must handle the result correctly, which means that, should it return a promise, we must resolve it first.

现在我们必须设置结果。 请记住,我们必须正确处理结果,这意味着,如果它返回承诺,我们必须首先解决它。

class PQ<T> {

  // ...
  
  private setResult = (value: T | any, state: States) => {
    const set = () => {
      if (this.state !== States.PENDING) {
        return null;
      }

      if (isThenable(value)) {
        return (value as Thenable<T>).then(this.resolve, this.reject);
      }

      this.value = value;
      this.state = state;

      return this.executeHandlers();
    };

    setTimeout(set, 0);
  };
}

First, we check if the state is not pending — if it is, then the promise is already settled and we can’t assign any new value to it.

首先,我们检查状态是否未决 -如果是,则该诺言已经兑现,并且我们无法为其分配任何新值。

Then we need to check if a value is a thenable. To put it simply, a thenable is an object with then as a method.

然后,我们需要检查一个值是否为thenable 。 简而言之, thenable是一个对象, 然后将其作为方法。

By convention, a thenable should behave like a promise. So in order to get the result, we will call then and pass as arguments this.resolve and this.reject.

按照惯例,一个守信者应该表现得像一个诺言。 因此,为了获得结果,我们将调用然后this.resolvethis.reject作为参数传递

Once the thenable settles, it will call one of our methods and give us the expected non-promise value.

一旦结算商品结算,它将调用我们的方法之一,并为我们提供预期的非承诺价值。

So now we have to check if an object is a thenable.

因此,现在我们必须检查一个对象是否是可塑的

describe('isThenable', () => {
  test('detects objects with a then method', () => {
    expect(isThenable({ then: () => null })).toBe(true);
    expect(isThenable(null)).toBe(false);
    expect(isThenable({})).toBe(false);
  });
});
const isFunction = (func: any) => typeof func === 'function';

const isObject = (supposedObject: any) =>
  typeof supposedObject === 'object' &&
  supposedObject !== null &&
  !Array.isArray(supposedObject);

const isThenable = (obj: any) => isObject(obj) && isFunction(obj.then);

It is important to realize that our promise will never be synchronous, even if the code inside the callback is.

重要的是要意识到,即使回调中的代码是同步的,我们的承诺也永远不会同步。

We are going to delay the execution until the next iteration of the event loop by using setTimeout.

我们将使用setTimeout将执行延迟到事件循环的下一个迭代。

Now the only thing left to do is to set our value and status and then execute the registered handlers.

现在剩下要做的就是设置我们的值和状态,然后执行注册的处理程序。

executeHandlers (executeHandlers)
class PQ<T> {

  // ...
  
  private executeHandlers = () => {
    if (this.state === States.PENDING) {
      return null;
    }

    this.handlers.forEach((handler) => {
      if (this.state === States.REJECTED) {
        return handler.onFail(this.value);
      }

      return handler.onSuccess(this.value);
    });

    this.handlers = [];
  };
}

Again, make sure the state is not pending.

再次,确保状态不是未决

The state of the promise dictates which function we are going to use.

承诺的状态决定了我们将要使用的功能。

If it’s resolved, we should execute onSuccess, otherwise — onFail.

如果解决了 ,我们应该执行onSuccess ,否则-onFail

Let’s now clear our array of handlers just to be safe and not to execute anything accidentally in the future. A handler can be attached and executed later anyways.

现在,让我们清除处理程序数组只是为了安全起见,并且将来不要执行任何意外操作。 无论如何,可以附加处理程序并在以后执行。

And that’s what we must discuss next: a way to attach our handler.

这就是我们接下来必须讨论的:附加处理程序的方法。

attachHandler (attachHandler)
class PQ<T> {

  // ...
  
  private attachHandler = (handler: Handler<T, any>) => {
    this.handlers = [...this.handlers, handler];

    this.executeHandlers();
  };
}

It really is as simple as it seems. We just add a handler to our handlers array and execute it. That’s it.

确实看起来很简单。 我们只是将一个处理程序添加到我们的处理程序数组中并执行它。 而已。

Now, to put it all together we need to implement the then method.

现在,将所有内容放在一起,我们需要实现then方法。

然后 (then)
class PQ<T> {

  // ...
  
  public then<U>(
    onSuccess?: HandlerOnSuccess<T, U>,
    onFail?: HandlerOnFail<U>,
  ) {
    return new PQ<U | T>((resolve, reject) => {
      return this.attachHandler({
        onSuccess: (result) => {
          if (!onSuccess) {
            return resolve(result);
          }

          try {
            return resolve(onSuccess(result));
          } catch (e) {
            return reject(e);
          }
        },
        onFail: (reason) => {
          if (!onFail) {
            return reject(reason);
          }

          try {
            return resolve(onFail(reason));
          } catch (e) {
            return reject(e);
          }
        },
      });
    });
  }
}

In then, we return a promise, and in the callback we attach a handler that is then used to wait for the current promise to be settled.

然后,我们返回一个Promise,并在回调中附加一个处理程序,该处理程序随后用于等待当前Promise的结算。

When that happens, either handler’s onSuccess or onFail will be executed and we will proceed accordingly.

发生这种情况时,将执行处理程序的onSuccessonFail ,我们将进行相应的处理。

One thing to remember here is that neither of the handlers passed to then is required. It is important, however, that we don’t try to execute something that might be undefined.

这里要记住的一件事是,不需要传递给那时的处理程序。 但是重要的是,我们不要试图执行可能未定义的操作

Also, in onFail when the handler is passed, we actually resolve the returned promise, because the error has been handled.

另外,在onFail传递处理程序时,我们实际上解决了返回的promise,因为错误已得到处理。

抓住 (catch)

Catch is actually just an abstraction over the then method.

实际上, catch只是对then方法的抽象。

class PQ<T> {

  // ...
  
  public catch<U>(onFail: HandlerOnFail<U>) {
    return this.then<U>(identity, onFail);
  }
}

That’s it.

而已。

最后 (Finally)

Finally is also just an abstraction over doing then(finallyCb, finallyCb), because it doesn’t really care about the result of the promise.

最后,这也是对那么做的抽象( finallyCbfinallyCb ),因为它并不真正在乎诺言的结果。

Actually, it also preserves the result of the previous promise and returns it. So whatever is being returned by the finallyCb doesn’t really matter.

实际上,它还保留了先前承诺的结果并将其返回。 因此, finallyCb返回的内容并不重要。

describe('PQ.prototype.finally', () => {
  test('it is called regardless of the promise state', () => {
    let counter = 0;
    return PQ.resolve(15)
      .finally(() => {
        counter += 1;
      })
      .then(() => {
        return PQ.reject(15);
      })
      .then(() => {
        // wont be called
        counter = 1000;
      })
      .finally(() => {
        counter += 1;
      })
      .catch((reason) => {
        expect(reason).toBe(15);
        expect(counter).toBe(2);
      });
  });
});
class PQ<T> {

  // ...
  

  public finally<U>(cb: Finally<U>) {
    return new PQ<U>((resolve, reject) => {
      let val: U | any;
      let isRejected: boolean;

      return this.then(
        (value) => {
          isRejected = false;
          val = value;
          return cb();
        },
        (reason) => {
          isRejected = true;
          val = reason;
          return cb();
        },
      ).then(() => {
        if (isRejected) {
          return reject(val);
        }

        return resolve(val);
      });
    });
  }
}
toString (toString)
describe('PQ.prototype.toString', () => {
  test('returns [object PQ]', () => {
    expect(new PQ<undefined>((resolve) => resolve()).toString()).toBe(
      '[object PQ]',
    );
  });
});
class PQ<T> {

  // ...
  
  public toString() {
    return `[object PQ]`;
  }
}

It will just return a string [object PQ].

它只会返回一个字符串[object PQ]

Having implemented the core of our promises, we can now implement some of the previously mentioned Bluebird methods, which will make operating on promises easier for us.

在实现了承诺的核心之后,我们现在可以实现一些前面提到的Bluebird方法,这将使我们更容易实现承诺。

其他方法 (Additional methods)

承诺解决 (Promise.resolve)

How it should work.

它应该如何工作。

describe('PQ.resolve', () => {
  test('resolves a value', () => {
    return PQ.resolve(5).then((value) => {
      expect(value).toBe(5);
    });
  });
});
class PQ<T> {

  // ...
  
  public static resolve<U = any>(value?: U | Thenable<U>) {
    return new PQ<U>((resolve) => {
      return resolve(value);
    });
  }
}
承诺拒绝 (Promise.reject)

How it should work.

它应该如何工作。

describe('PQ.reject', () => {
  test('rejects a value', () => {
    return PQ.reject(5).catch((value) => {
      expect(value).toBe(5);
    });
  });
});
class PQ<T> {

  // ...
  
  public static reject<U>(reason?: any) {
    return new PQ<U>((resolve, reject) => {
      return reject(reason);
    });
  }
}
无极 (Promise.all)

How it should work.

它应该如何工作。

describe('PQ.all', () => {
  test('resolves a collection of promises', () => {
    return PQ.all([PQ.resolve(1), PQ.resolve(2), 3]).then((collection) => {
      expect(collection).toEqual([1, 2, 3]);
    });
  });

  test('rejects if one item rejects', () => {
    return PQ.all([PQ.resolve(1), PQ.reject(2)]).catch((reason) => {
      expect(reason).toBe(2);
    });
  });
});
class PQ<T> {

  // ...
  
  public static all<U = any>(collection: (U | Thenable<U>)[]) {
    return new PQ<U[]>((resolve, reject) => {
      if (!Array.isArray(collection)) {
        return reject(new TypeError('An array must be provided.'));
      }

      let counter = collection.length;
      const resolvedCollection: U[] = [];

      const tryResolve = (value: U, index: number) => {
        counter -= 1;
        resolvedCollection[index] = value;

        if (counter !== 0) {
          return null;
        }

        return resolve(resolvedCollection);
      };

      return collection.forEach((item, index) => {
        return PQ.resolve(item)
          .then((value) => {
            return tryResolve(value, index);
          })
          .catch(reject);
      });
    });
  }
}

I believe the implementation is pretty straightforward.

我相信实施非常简单。

Starting at collection.length, we count down with each tryResolve until we get to 0, which means that every item of the collection has been resolved. We then resolve the newly created collection.

collection.length开始我们对每个tryResolve进行递减计数,直到达到0,这意味着该集合的每个项目都已解决。 然后,我们解析新创建的集合。

承诺 (Promise.any)

How it should work.

它应该如何工作。

describe('PQ.any', () => {
  test('resolves the first value', () => {
    return PQ.any<number>([
      PQ.resolve(1),
      new PQ((resolve) => setTimeout(resolve, 15)),
    ]).then((val) => expect(val).toBe(1));
  });

  test('rejects if the first value rejects', () => {
    return PQ.any([
      new PQ((resolve) => setTimeout(resolve, 15)),
      PQ.reject(1),
    ]).catch((reason) => {
      expect(reason).toBe(1);
    });
  });
});
class PQ<T> {

  // ...

  public static any<U = any>(collection: (U | Thenable<U>)[]) {
    return new PQ<U>((resolve, reject) => {
      return collection.forEach((item) => {
        return PQ.resolve(item)
          .then(resolve)
          .catch(reject);
      });
    });
  }
}

We simply wait for the first value to resolve and return it in a promise.

我们只是等待第一个值解析并在promise中返回它。

承诺道具 (Promise.props)

How it should work.

它应该如何工作。

describe('PQ.props', () => {
  test('resolves object correctly', () => {
    return PQ.props<{ test: number; test2: number }>({
      test: PQ.resolve(1),
      test2: PQ.resolve(2),
    }).then((obj) => {
      return expect(obj).toEqual({ test: 1, test2: 2 });
    });
  });

  test('rejects non objects', () => {
    return PQ.props([]).catch((reason) => {
      expect(reason).toBeInstanceOf(TypeError);
    });
  });
});
class PQ<T> {

  // ...
  
  public static props<U = any>(obj: object) {
    return new PQ<U>((resolve, reject) => {
      if (!isObject(obj)) {
        return reject(new TypeError('An object must be provided.'));
      }

      const resolvedObject = {};

      const keys = Object.keys(obj);
      const resolvedValues = PQ.all<string>(keys.map((key) => obj[key]));

      return resolvedValues
        .then((collection) => {
          return collection.map((value, index) => {
            resolvedObject[keys[index]] = value;
          });
        })
        .then(() => resolve(resolvedObject as U))
        .catch(reject);
    });
  }
}

We iterate over keys of the passed object, resolving every value. We then assign the values to the new object and resolve a promise with it.

我们遍历传递对象的键,以解析每个值。 然后,我们将值分配给新对象,并用它解决承诺。

Promise.prototype.spread (Promise.prototype.spread)

How it should work.

它应该如何工作。

describe('PQ.protoype.spread', () => {
  test('spreads arguments', () => {
    return PQ.all<number>([1, 2, 3]).spread((...args) => {
      expect(args).toEqual([1, 2, 3]);
      return 5;
    });
  });

  test('accepts normal value (non collection)', () => {
    return PQ.resolve(1).spread((one) => {
      expect(one).toBe(1);
    });
  });
});
class PQ<T> {

  // ...
  
  public spread<U>(handler: (...args: any[]) => U) {
    return this.then<U>((collection) => {
      if (Array.isArray(collection)) {
        return handler(...collection);
      }

      return handler(collection);
    });
  }
}
承诺延迟 (Promise.delay)

How it should work.

它应该如何工作。

describe('PQ.delay', () => {
  test('waits for the given amount of miliseconds before resolving', () => {
    return new PQ<string>((resolve) => {
      setTimeout(() => {
        resolve('timeout');
      }, 50);

      return PQ.delay(40).then(() => resolve('delay'));
    }).then((val) => {
      expect(val).toBe('delay');
    });
  });

  test('waits for the given amount of miliseconds before resolving 2', () => {
    return new PQ<string>((resolve) => {
      setTimeout(() => {
        resolve('timeout');
      }, 50);

      return PQ.delay(60).then(() => resolve('delay'));
    }).then((val) => {
      expect(val).toBe('timeout');
    });
  });
});
class PQ<T> {

  // ...
  
  public static delay(timeInMs: number) {
    return new PQ((resolve) => {
      return setTimeout(resolve, timeInMs);
    });
  }
}

By using setTimeout, we simply delay the execution of the resolve function by the given number of milliseconds.

通过使用setTimeout,我们只需将解析函数的执行延迟给定的毫秒数即可。

Promise.prototype.timeout (Promise.prototype.timeout)

How it should work.

它应该如何工作。

describe('PQ.prototype.timeout', () => {
  test('rejects after given timeout', () => {
    return new PQ<number>((resolve) => {
      setTimeout(resolve, 50);
    })
      .timeout(40)
      .catch((reason) => {
        expect(reason).toBeInstanceOf(PQ.errors.TimeoutError);
      });
  });

  test('resolves before given timeout', () => {
    return new PQ<number>((resolve) => {
      setTimeout(() => resolve(500), 500);
    })
      .timeout(600)
      .then((value) => {
        expect(value).toBe(500);
      });
  });
});
class PQ<T> {

  // ...
  
  public timeout(timeInMs: number) {
    return new PQ<T>((resolve, reject) => {
      const timeoutCb = () => {
        return reject(new PQ.errors.TimeoutError());
      };

      setTimeout(timeoutCb, timeInMs);

      return this.then(resolve);
    });
  }
}

This one is a bit tricky.

这个有点棘手。

If the setTimeout executes faster than then in our promise, it will reject the promise with our special error.

如果执行的setTimeout那么在我们的承诺更快,它将拒绝与我们特殊的错误的诺言。

承诺 (Promise.promisify)

How it should work.

它应该如何工作。

describe('PQ.promisify', () => {
  test('works', () => {
    const getName = (firstName, lastName, callback) => {
      return callback(null, `${firstName} ${lastName}`);
    };

    const fn = PQ.promisify<string>(getName);
    const firstName = 'Maciej';
    const lastName = 'Cieslar';

    return fn(firstName, lastName).then((value) => {
      return expect(value).toBe(`${firstName} ${lastName}`);
    });
  });
});
class PQ<T> {

  // ...
  
  public static promisify<U = any>(
    fn: (...args: any[]) => void,
    context = null,
  ) {
    return (...args: any[]) => {
      return new PQ<U>((resolve, reject) => {
        return fn.apply(context, [
          ...args,
          (err: any, result: U) => {
            if (err) {
              return reject(err);
            }

            return resolve(result);
          },
        ]);
      });
    };
  }
}

We apply to the function all the passed arguments, plus — as the last one — we give the error-first callback.

我们将所有传递的参数应用于该函数,再加上最后一个参数,我们给出了错误优先的回调函数。

Promise.promisifyAll (Promise.promisifyAll)

How it should work.

它应该如何工作。

describe('PQ.promisifyAll', () => {
  test('promisifies a object', () => {
    const person = {
      name: 'Maciej Cieslar',
      getName(callback) {
        return callback(null, this.name);
      },
    };

    const promisifiedPerson = PQ.promisifyAll<{
      getNameAsync: () => PQ<string>;
    }>(person);

    return promisifiedPerson.getNameAsync().then((name) => {
      expect(name).toBe('Maciej Cieslar');
    });
  });
});
class PQ<T> {

  // ...
  
  public static promisifyAll<U>(obj: any): U {
    return Object.keys(obj).reduce((result, key) => {
      let prop = obj[key];

      if (isFunction(prop)) {
        prop = PQ.promisify(prop, obj);
      }

      result[`${key}Async`] = prop;

      return result;
    }, {}) as U;
  }
}

We iterate over the keys of the object and promisify its methods and add to each name of the method word Async.

我们遍历对象的键并赋值其方法,并将方法词Async添加到方法的每个名称中。

结语 (Wrapping up)

Presented here were but a few amongst all of the Bluebird API methods, so I strongly encourage you to explore, play around with, and try implementing the rest of them.

这里介绍的只是所有Bluebird API方法中的几个,因此我强烈建议您探索,尝试并尝试实现其余的方法。

It might seem hard at first but don’t get discouraged — it would be worthless if it were it easy.

乍一看似乎很困难,但不要气—-如果这很容易,那将毫无价值。

Thank you very much for reading! I hope you found this article informative and that it helped you get a grasp of the concept of promises, and that from now on you will feel more comfortable using them or simply writing asynchronous code.

非常感谢您的阅读! 我希望您能从这篇文章中学到很多知识,并能帮助您理解Promise的概念,从现在开始,您将更容易使用Promise或简单地编写异步代码。

If you have any questions or comments, feel free to put them in the comment section below or send me a message.

如果您有任何问题或意见,请随时将其放在下面的评论部分或给我发送消息

Check out my social media!

看看我的社交媒体

Join my newsletter!

加入我的时事通讯

Originally published at www.mcieslar.com on August 4, 2018.

最初于2018年8月4日发布在www.mcieslar.com上。

翻译自: https://www.freecodecamp.org/news/how-to-implement-promises-in-javascript-1ce2680a7f51/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值