From 35800962c16a33eb8e9ff1adfd428cf00bb670d3 Mon Sep 17 00:00:00 2001 From: Moti Zilberman Date: Thu, 17 Feb 2022 09:13:35 -0800 Subject: [PATCH] Deprecate nonstandard Promise.prototype.done Summary: Deprecates the nonstandard `Promise.prototype.done` method. This also removes one call site within React Native itself that relied on this method. As part of this we are also removing React Native's custom Flow definition for `Promise` in favour of the standard one built into Flow. This will flag uses of `done` as type errors for anyone using the default app template's `.flowconfig`. In a future release of React Native, we will remove the `done` method from the built-in `Promise` polyfill. Changelog: [General][Deprecated] - Deprecate the Promise.prototype.done method and log a warning when it's called in development. Reviewed By: yungsters Differential Revision: D34222667 fbshipit-source-id: 4b9708ac20c45b3966fdb93e883ab7f8d80017c1 --- Libraries/Core/polyfillPromise.js | 32 ++++++++++++++ Libraries/Interaction/InteractionManager.js | 11 ----- Libraries/Interaction/TaskQueue.js | 9 ++-- flow/Promise.js | 47 --------------------- 4 files changed, 37 insertions(+), 62 deletions(-) delete mode 100644 flow/Promise.js diff --git a/Libraries/Core/polyfillPromise.js b/Libraries/Core/polyfillPromise.js index 857ea8bdb50..a35b581f7ff 100644 --- a/Libraries/Core/polyfillPromise.js +++ b/Libraries/Core/polyfillPromise.js @@ -11,6 +11,7 @@ 'use strict'; const {polyfillGlobal} = require('../Utilities/PolyfillFunctions'); +const warnOnce = require('../Utilities/warnOnce'); /** * Set up Promise. The native Promise implementation throws the following error: @@ -36,3 +37,34 @@ if (global?.HermesInternal?.hasPromise?.()) { } else { polyfillGlobal('Promise', () => require('../Promise')); } + +if (__DEV__) { + // $FlowFixMe + const done = Promise.prototype.done; + if (done != null) { + let depth = 0; + /* eslint-disable no-extend-native */ + // $FlowFixMe + Promise.prototype.done = function () { + ++depth; + try { + // Avoid infinite recursion if done() happens to be triggered by warnOnce. + if (depth === 1) { + // Warn once per unique call stack. Not super efficient, but we're in + // __DEV__ and .done() calls are rare to begin with. + const key = new Error().stack; + warnOnce( + key, + 'Promise.prototype.done(): This nonstandard polyfill ' + + 'has been deprecated and will be removed in a future release. ' + + 'Please instead use `.then()`.', + ); + } + } finally { + --depth; + } + return done.apply(this, arguments); + }; + /* eslint-enable no-extend-native */ + } +} diff --git a/Libraries/Interaction/InteractionManager.js b/Libraries/Interaction/InteractionManager.js index 887fa8728d6..0122ab70d63 100644 --- a/Libraries/Interaction/InteractionManager.js +++ b/Libraries/Interaction/InteractionManager.js @@ -91,7 +91,6 @@ const InteractionManager = { onFulfill?: ?(void) => ?(Promise | U), onReject?: ?(error: mixed) => ?(Promise | U), ) => Promise, - done: () => void, cancel: () => void, ... } { @@ -110,16 +109,6 @@ const InteractionManager = { return { // $FlowFixMe[method-unbinding] added when improving typing for this parameters then: promise.then.bind(promise), - done: (...args) => { - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - if (promise.done) { - return promise.done(...args); - } else { - console.warn( - 'Tried to call done when not supported by current Promise implementation.', - ); - } - }, cancel: function () { _taskQueue.cancelTasks(tasks); }, diff --git a/Libraries/Interaction/TaskQueue.js b/Libraries/Interaction/TaskQueue.js index c7f690481ed..0f209a7d945 100644 --- a/Libraries/Interaction/TaskQueue.js +++ b/Libraries/Interaction/TaskQueue.js @@ -171,10 +171,11 @@ class TaskQueue { this.hasTasksToProcess() && this._onMoreTasks(); }) .catch(ex => { - ex.message = `TaskQueue: Error resolving Promise in task ${task.name}: ${ex.message}`; - throw ex; - }) - .done(); + setTimeout(() => { + ex.message = `TaskQueue: Error resolving Promise in task ${task.name}: ${ex.message}`; + throw ex; + }, 0); + }); } } diff --git a/flow/Promise.js b/flow/Promise.js deleted file mode 100644 index 05d7e42af6f..00000000000 --- a/flow/Promise.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict - * @format - */ - -// These annotations are copy/pasted from the built-in Flow definitions for -// Native Promises with some non-standard APIs added in -declare class Promise<+R> { - constructor( - callback: ( - resolve: (result?: Promise | R) => void, - reject: (error?: any) => void, - ) => mixed, - ): void; - - then( - onFulfill?: ?(value: R) => Promise | ?U, - onReject?: ?(error: any) => Promise | ?U, - ): Promise; - - catch(onReject?: (error: any) => ?Promise | U): Promise; - - static resolve(object?: Promise | T): Promise; - static reject(error?: any): Promise; - - static all>( - promises: T, - ): Promise<$TupleMap>; - static race(promises: Array>): Promise; - - // Non-standard APIs - - // See https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/__forks__/Promise.native.js#L21 - finally(onFinally?: ?(value: any) => Promise | U): Promise; - - done( - onFulfill?: ?(value: R) => mixed, - onReject?: ?(error: any) => mixed, - ): void; - - static cast(object?: T): Promise; -}