React.js 告诉你取消 Promise。官方承诺无法取消。我该怎么办?

问题描述 投票:0回答:2

为了防止对已卸载的 React 组件进行虚拟更新,React 会告诉您在卸载组件时取消组件上的任何待处理的 Promise(例如用于获取附加数据的 Promise)。使用 Bluebird Promise 可以很容易地实现这一点,它有一个 .cancel()

 方法,导致 
.then()
.catch()
 处理程序永远不会响应。

但是,ES6 Promise 不支持取消。此外,ES7 的

async

await
 仅使用原生 Promises,不支持任何直接替换(例如 Bluebird)。这意味着,如果您希望能够在 React 中取消 Promise(正如他们告诉您的那样),您必须使用 
.then()
.catch()
,并且还必须在 
fetch()
 等原生 Promise 方法上放置一个中间人,以便可以取消。

这真的是 React 所期望的吗?

javascript reactjs es6-promise cancellation
2个回答
0
投票
仅供大家参考。 使用

CPromise 包,您可以取消您的承诺链,包括嵌套的承诺链。它支持 AbortController 和生成器作为 ECMA 异步函数的替代品。目前该项目处于测试阶段。

发电机使用

现场演示

import CPromise from "c-promise2"; const chain = CPromise.resolve() .then(function* () { const value1 = yield new CPromise((resolve, reject, { onCancel }) => { const timer = setTimeout(resolve, 1000, 3); onCancel(() => { console.log("timer cleared"); clearTimeout(timer); }); }); // Run promises in parallel using CPromise.all (shortcut syntax) const [value2, value3] = yield [ CPromise.delay(1000, 4), CPromise.delay(1000, 5) ]; return value1 + value2 + value3; }) .then( (value) => { console.log(`Done: ${value}`); // Done: 12 (without calling cancel) }, (err) => { console.log(`Failed: ${err}`); // Failed: CanceledError: canceled } ); setTimeout(() => chain.cancel(), 100);
输出:

timer cleared Failed: CanceledError: canceled

所有阶段都完全可以取消/中止。 这是将其与 React 一起使用的示例Live Demo

export class TestComponent extends React.Component { state = {}; async componentDidMount() { console.log("mounted"); this.controller = new CPromise.AbortController(); try { const json = await this.myAsyncTask( "https://run.mocky.io/v3/7b038025-fc5f-4564-90eb-4373f0721822?mocky-delay=2s" ); console.log("json:", json); await this.myAsyncTaskWithDelay(1000, 123); // just another async task this.setState({ text: JSON.stringify(json) }); } catch (err) { if (CPromise.isCanceledError(err)) { console.log("tasks terminated"); } } } myAsyncTask(url) { return CPromise.from(function* () { const response = yield cpFetch(url); // cancellable request return yield response.json(); }).listen(this.controller.signal); } myAsyncTaskWithDelay(ms, value) { return new CPromise((resolve, reject, { onCancel }) => { const timer = setTimeout(resolve, ms, value); onCancel(() => { console.log("timeout cleared"); clearTimeout(timer); }); }).listen(this.controller.signal); } render() { return ( <div> AsyncComponent: <span>{this.state.text || "fetching..."}</span> </div> ); } componentWillUnmount() { console.log("unmounted"); this.controller.abort(); // kill all pending tasks } }

使用Hooks和cancel

方法

import React, { useEffect, useState } from "react"; import CPromise from "c-promise2"; import cpFetch from "cp-fetch"; export function TestComponent(props) { const [text, setText] = useState("fetching..."); useEffect(() => { console.log("mount"); const promise = cpFetch(props.url) .then(function* (response) { const json = yield response.json(); setText(`Delay for 2000ms...`); yield CPromise.delay(2000); setText(`Success: ${JSON.stringify(json)}`); }) .canceled() .catch((err) => { setText(`Failed: ${err}`); }); return () => { console.log("unmount"); promise.cancel(); }; }, [props.url]); return <p>{text}</p>; }
    

0
投票
最新的 React 文档(2023 年 8 月),针对此问题提出了 2 个解决方案:

  1. 创建一个新的“忽略”变量,在效果清理期间将其设置为 true。当 Promise 返回时,根据变量忽略结果。

  2. 使用

    AbortController API 取消 Promise。

在 React 文档的

此页上查看“Challenge 4 of 4”的解决方案。

© www.soinside.com 2019 - 2024. All rights reserved.