Javascript 如何使用带有react挂钩的互斥锁?

Javascript 如何使用带有react挂钩的互斥锁?,javascript,react-hooks,async-mutex,Javascript,React Hooks,Async Mutex,我想确保代码块不会并发运行。异步互斥体库似乎不适合我。这里有一个最小的复制: /* eslint-disable */ import React, { useState, useEffect } from "react"; var Mutex = require("async-mutex").Mutex; const Timer = () => { const [isActive, setIsActive] = useState(false);

我想确保代码块不会并发运行。异步互斥体库似乎不适合我。这里有一个最小的复制:

/* eslint-disable */
import React, { useState, useEffect } from "react";
var Mutex = require("async-mutex").Mutex;

const Timer = () => {
  const [isActive, setIsActive] = useState(false);
  const mutex = new Mutex();

  useEffect(() => {
    mutex.runExclusive(async function () {
      console.log("starting", isActive);
      await new Promise((resolve) => setTimeout(resolve, 1000));
      console.log("ending", isActive);
    });
  }, [isActive]);

  return (
    <div className="app">
      <button onClick={() => setIsActive((prev) => !prev)} type="button">
        {isActive ? "Active" : "Inactive"}
      </button>
    </div>
  );
};

export { Timer as default };

你可以看到这些是交错的


我如何才能强制它不同时运行?

问题很可能是因为每次重新渲染时都会分配互斥,从而为您提供一个新实例

  • 您可以移动
    const mutex=new mutex()从组件中删除,为所有计时器组件提供一个全局版本,这意味着计时器可能会相互阻塞

  • 通过将互斥实例包装在
    React.usemo
    hook中,可以使互斥实例在整个重新渲染过程中保持稳定,这将使每个计时器组件都有自己的互斥,这意味着计时器不能相互阻止,只能阻止其自身

  • 将组件内的声明替换为:

    const mutex=React.usemo(()=>new mutex(),[]);
    
    然后将互斥体添加到
    useffect
    中的依赖项数组中:

    useffect(()=>{
    //你的代码
    },[isActive,mutex]);
    
    问题最有可能是因为每次重新渲染时都会分配互斥,从而为您提供一个新实例

  • 您可以移动
    const mutex=new mutex()从组件中删除,为所有计时器组件提供一个全局版本,这意味着计时器可能会相互阻塞

  • 通过将互斥实例包装在
    React.usemo
    hook中,可以使互斥实例在整个重新渲染过程中保持稳定,这将使每个计时器组件都有自己的互斥,这意味着计时器不能相互阻止,只能阻止其自身

  • 将组件内的声明替换为:

    const mutex=React.usemo(()=>new mutex(),[]);
    
    然后将互斥体添加到
    useffect
    中的依赖项数组中:

    useffect(()=>{
    //你的代码
    },[isActive,mutex]);
    

    import React,{useState,useffect,useRef,usemo}来自“React”;
    var Mutex=require(“异步互斥”).Mutex;
    常量计时器=()=>{
    常量[isActive,setIsActive]=useState(false);
    const mutex=useRef(new mutex());
    //或者换一种方式,它可能会更有表现力,
    //虽然互斥类有一个几乎为空的构造函数
    //const mutex=usemo(()=>new mutex(),[]);
    useffect(()=>{
    mutex.current.runExclusive(异步函数(){
    console.log(“启动”,isActive);
    等待新承诺((resolve)=>setTimeout(resolve,1000));
    console.log(“结束”,isActive);
    });
    },[i活跃];
    返回(
    setIsActive((prev)=>!prev)}type=“button”>
    {isActive?“Active”:“Inactive”}
    );
    };
    导出{Timer as default};
    
    如果要取消上一个函数调用并支持卸载时异步例程清理():

    /*eslint禁用*/
    从“React”导入React,{useState,useffect};
    从“c-promise2”导入CPromise;
    从“use-async-effect2”导入{UseAncyEffect,UseAncyCallback};
    导出默认函数计时器(){
    常量[isActive,setIsActive]=useState(false);
    const callback=useAncyncCallback(
    函数*(){
    console.log(“启动”,isActive);
    收益率c减少延迟(1000);
    console.log(“结束”,isActive);
    },
    {combine:true}
    );
    使用异步效应(
    函数*(){
    控制台日志(“安装”);
    屈服回调();
    },
    [活动]
    );
    返回(
    setIsActive((prev)=>!prev)}type=“button”>
    {isActive?“Active”:“Inactive”}
    );
    }
    

    import React,{useState,useffect,useRef,usemo}来自“React”;
    var Mutex=require(“异步互斥”).Mutex;
    常量计时器=()=>{
    常量[isActive,setIsActive]=useState(false);
    const mutex=useRef(new mutex());
    //或者换一种方式,它可能会更有表现力,
    //虽然互斥类有一个几乎为空的构造函数
    //const mutex=usemo(()=>new mutex(),[]);
    useffect(()=>{
    mutex.current.runExclusive(异步函数(){
    console.log(“启动”,isActive);
    等待新承诺((resolve)=>setTimeout(resolve,1000));
    console.log(“结束”,isActive);
    });
    },[i活跃];
    返回(
    setIsActive((prev)=>!prev)}type=“button”>
    {isActive?“Active”:“Inactive”}
    );
    };
    导出{Timer as default};
    
    如果要取消上一个函数调用并支持卸载时异步例程清理():

    /*eslint禁用*/
    从“React”导入React,{useState,useffect};
    从“c-promise2”导入CPromise;
    从“use-async-effect2”导入{UseAncyEffect,UseAncyCallback};
    导出默认函数计时器(){
    常量[isActive,setIsActive]=useState(false);
    const callback=useAncyncCallback(
    函数*(){
    console.log(“启动”,isActive);
    收益率c减少延迟(1000);
    console.log(“结束”,isActive);
    },
    {combine:true}
    );
    使用异步效应(
    函数*(){
    控制台日志(“安装”);
    屈服回调();
    },
    [活动]
    );
    返回(
    setIsActive((prev)=>!prev)}type=“button”>
    {isActive?“Active”:“Inactive”}
    );
    }
    
    starting 
    true
    starting 
    false
    starting 
    true
    ending 
    true
    ending 
    false
    
    import React, { useState, useEffect, useRef, useMemo} from "react";
    var Mutex = require("async-mutex").Mutex;
    
    const Timer = () => {
      const [isActive, setIsActive] = useState(false);
      const mutex = useRef(new Mutex());
      // Or another way, it might be a bit more performant,
      // although the Mutex class has an almost empty constructor
      // const mutex= useMemo(()=> new Mutex(), []);
    
      useEffect(() => {
        mutex.current.runExclusive(async function () {
          console.log("starting", isActive);
          await new Promise((resolve) => setTimeout(resolve, 1000));
          console.log("ending", isActive);
        });
      }, [isActive]);
    
      return (
        <div className="app">
          <button onClick={() => setIsActive((prev) => !prev)} type="button">
            {isActive ? "Active" : "Inactive"}
          </button>
        </div>
      );
    };
    
    export { Timer as default };
    
    /* eslint-disable */
    import React, { useState, useEffect } from "react";
    import CPromise from "c-promise2";
    import { useAsyncEffect, useAsyncCallback } from "use-async-effect2";
    
    export default function Timer() {
      const [isActive, setIsActive] = useState(false);
    
      const callback = useAsyncCallback(
        function* () {
          console.log("starting", isActive);
          yield CPromise.delay(1000);
          console.log("ending", isActive);
        },
        { combine: true }
      );
    
      useAsyncEffect(
        function* () {
          console.log("mount");
          yield callback();
        },
        [isActive]
      );
    
      return (
        <div className="app">
          <button onClick={() => setIsActive((prev) => !prev)} type="button">
            {isActive ? "Active" : "Inactive"}
          </button>
        </div>
      );
    }