UNPKG

@itwin/ecschema-metadata

Version:

ECObjects core concepts in typescript

90 lines 4.09 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module Utils */ /** * Similar to a normal Promise, a DelayedPromise represents the eventual completion (or failure) * and resulting value of an asynchronous operation ***that has not yet started***. * * The asynchronous operation behind a DelayedPromise will start when any of the following occurs: * - The DelayedPromise is `await`ed. * - A callback is attached via `.then()` or `.catch(() => { })`. * - The asynchronous operation is explicitly started via `.start()` * * Just as normal Promises will never return to their pending state once fulfilled or rejected, * a DelayedPromise will never re-execute its asynchronous operation more than **once**. * * Ultimately, a DelayedPromise is nothing more than some syntactic sugar that allows you to * represent an (asynchronously) lazily-loaded value as an instance property instead of a method. * You could also accomplish something similar by defining an async function as a property getter. * However, since a property defined as a DelayedPromise will not start simply by being accessed, * additional (non-lazily-loaded) "nested" properties can be added. * * [!alert text="*Remember:* Unlike regular Promises in JavaScript, DelayedPromises represent processes that **may not** already be happening." kind="warning"] * @internal */ export class DelayedPromise { /** * Constructs a DelayedPromise object. * @param startCallback The asynchronous callback to execute when this DelayedPromise should be "started". */ constructor(startCallback) { let pending; this.start = async () => { pending = pending || startCallback(); return pending; }; } // We need this in order to fulfill the Promise interface defined in lib.es2015.symbol.wellknown.d.ts [Symbol.toStringTag] = "Promise"; /** * Explicitly starts the asynchronous operation behind this DelayedPromise (if it hasn't started already). */ start; /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. * @param onrejected The callback to execute when the Promise is rejected. * @return A Promise for the completion of which ever callback is executed. */ async then(onfulfilled, onrejected) { return this.start().then(onfulfilled, onrejected); } /** * Attaches a callback for only the rejection of the Promise. * @param onrejected The callback to execute when the Promise is rejected. * @return A Promise for the completion of the callback. */ async catch(onrejected) { return this.start().catch(onrejected); } /** * Attaches a callback for only the finally clause of the Promise. * @param onrejected The callback to execute when the Promise is finalized. * @return A Promise for the completion of the callback. */ async finally(onFinally) { return this.start().finally(onFinally); } } // Because the property getters that wrap `props` are dynamically added, TypeScript isn't aware of them. // So by defining this as a class _expression_, we can cast the constructed type to Readonly<TProps> & DelayedPromise<TPayload> /** * @internal */ // eslint-disable-next-line @typescript-eslint/naming-convention export const DelayedPromiseWithProps = (class extends DelayedPromise { constructor(props, cb) { super(cb); const handler = { get: (target, name) => { return (name in this) ? this[name] : target[name]; }, }; return new Proxy(props, handler); } }); //# sourceMappingURL=DelayedPromise.js.map