UniTask

2025-12-11 0 312

UniTask

为统一提供有效的无异步分配/等待集成。

  • 基于structUniTask <T>自定义异步分组以实现零分配
  • 使所有的统一异步和共同点等待
  • 基于playerloop任务UniTask .YieldUniTask .DelayUniTask .DelayFrame等),可以更换所有Coroutine操作
  • Monobehaviour消息事件和UGUI事件(等待/异步)可以实现
  • 完全在Unity的PlayerLoop上运行,因此不使用线程并在WebGL,Wasm等上运行。
  • 异步LINQ,带有通道和异步性Property
  • 任务跟踪器窗口以防止内存泄漏
  • 高度兼容的行为与任务/valueTask/ivaluetasksource

有关技术详细信息,请参阅博客文章: UniTask V2 – 零分配异步/等待Unity,以及异步LINQ
有关高级提示,请参见博客文章:通过异步装饰器图案扩展Unitywebrequest – UniTask的高级技术

目录

  • 入门
  • UniTask和异步的基础知识
  • 取消和例外处理
  • 超时处理
  • 进步
  • PlayerLoop
  • 异步void vs async UniTask void
  • UniTask跟踪器
  • 外部资产
  • 异步和异步LINQ
  • 等待事件
  • 渠道
  • VS等待
  • 用于单元测试
  • ThreadPool限制
  • ienumerator.to UniTask限制
  • 对于Unityeditor
  • 与标准任务API进行比较
  • 合并配置
  • 分配分配器
  • UniTask同步context
  • API参考
  • UPM软件包
    • 通过git URL安装
  • .NET核心
  • 执照

入门

通过UPM软件包安装git参考或资产软件包( UniTask .*.*.*.unitypackage ),可在UniTask /发布中提供。

UniTask ), it is unity specialized lightweight alternative of Task<T>
// zero allocation and fast excution for zero overhead async/await integrate with Unity
async UniTask <string> DemoAsync()
{
// You can await Unity\’s AsyncObject
var asset = await Resources.LoadAsync<TextAsset>(\”foo\”);
var txt = (await UnityWebRequest.Get(\”https://…\”).SendWebRequest()).downloadHandler.text;
await SceneManager.LoadSceneAsync(\”scene2\”);

// .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
// after Unity 2022.2, you can use `destroyCancellationToken` in MonoBehaviour
var asset2 = await Resources.LoadAsync<TextAsset>(\”bar\”).WithCancellation(this.GetCancellationTokenOnDestroy());

// .To UniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
var asset3 = await Resources.LoadAsync<TextAsset>(\”baz\”).To UniTask (Progress.Create<float>(x => Debug.Log(x)));

// await frame-based operation like a coroutine
await UniTask .DelayFrame(100);

// replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
await UniTask .Delay(TimeSpan.FromSeconds(10), ignoreTimeScale: false);

// yield any playerloop timing(PreUpdate, Update, LateUpdate, etc…)
await UniTask .Yield(PlayerLoopTiming.PreLateUpdate);

// replacement of yield return null
await UniTask .Yield();
await UniTask .NextFrame();

// replacement of WaitForEndOfFrame
#if UNITY_2023_1_OR_NEWER
await UniTask .WaitForEndOfFrame();
#else
// requires MonoBehaviour(CoroutineRunner))
await UniTask .WaitForEndOfFrame(this); // this is MonoBehaviour
#endif

// replacement of yield return new WaitForFixedUpdate(same as UniTask .Yield(PlayerLoopTiming.FixedUpdate))
await UniTask .WaitForFixedUpdate();

// replacement of yield return WaitUntil
await UniTask .WaitUntil(() => isActive == false);

// special helper of WaitUntil
await UniTask .WaitUntilValueChanged(this, x => x.isActive);

// You can await IEnumerator coroutines
await FooCoroutineEnumerator();

// You can await a standard task
await Task.Run(() => 100);

// Multithreading, run on ThreadPool under this code
await UniTask .SwitchToThreadPool();

/* work on ThreadPool */

// return to MainThread(same as `ObserveOnMainThread` in UniRx)
await UniTask .SwitchToMainThread();

// get async webrequest
async UniTask <string> GetTextAsync(UnityWebRequest req)
{
var op = await req.SendWebRequest();
return op.downloadHandler.text;
}

var task1 = GetTextAsync(UnityWebRequest.Get(\”http://goog*l*e*.com\”));
var task2 = GetTextAsync(UnityWebRequest.Get(\”http://*bi*ng.co*m\”));
var task3 = GetTextAsync(UnityWebRequest.Get(\”http://yahoo***.com\”));

// concurrent async-wait and get results easily by tuple syntax
var (google, bing, yahoo) = await UniTask .WhenAll(task1, task2, task3);

// shorthand of WhenAll, tuple can await directly
var (google2, bing2, yahoo2) = await (task1, task2, task3);

// return async-value.(or you can use ` UniTask `(no result), ` UniTask Void`(fire and forget)).
return (asset as TextAsset)?.text ?? throw new InvalidOperationException(\”Asset not found\”);
}\”>

 // extension awaiter/methods can be used by this namespace
using Cysharp . Threading . Tasks ;

// You can return type as struct UniTask <T>(or UniTask ), it is unity specialized lightweight alternative of Task<T>
// zero allocation and fast excution for zero overhead async/await integrate with Unity
async UniTask < string > DemoAsync ( )
{
    // You can await Unity\'s AsyncObject
    var asset = await Resources . LoadAsync < TextAsset > ( \"foo\" ) ;
    var txt = ( await UnityWebRequest . Get ( \"https://...\" ) . SendWebRequest ( ) ) . downloadHandler . text ;
    await SceneManager . LoadSceneAsync ( \"scene2\" ) ;

    // .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
    // after Unity 2022.2, you can use `destroyCancellationToken` in MonoBehaviour
    var asset2 = await Resources . LoadAsync < TextAsset > ( \"bar\" ) . WithCancellation ( this . GetCancellationTokenOnDestroy ( ) ) ;

    // .To UniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
    var asset3 = await Resources . LoadAsync < TextAsset > ( \"baz\" ) . To UniTask ( Progress . Create < float > ( x => Debug . Log ( x ) ) ) ;

    // await frame-based operation like a coroutine
    await UniTask . DelayFrame ( 100 ) ; 

    // replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
    await UniTask . Delay ( TimeSpan . FromSeconds ( 10 ) , ignoreTimeScale : false ) ;
    
    // yield any playerloop timing(PreUpdate, Update, LateUpdate, etc...)
    await UniTask . Yield ( PlayerLoopTiming . PreLateUpdate ) ;

    // replacement of yield return null
    await UniTask . Yield ( ) ;
    await UniTask . NextFrame ( ) ;

    // replacement of WaitForEndOfFrame
#if UNITY_2023_1_OR_NEWER
    await UniTask . WaitForEndOfFrame ( ) ;
#else
    // requires MonoBehaviour(CoroutineRunner))
    await UniTask . WaitForEndOfFrame ( this ) ; // this is MonoBehaviour
#endif

    // replacement of yield return new WaitForFixedUpdate(same as UniTask .Yield(PlayerLoopTiming.FixedUpdate))
    await UniTask . WaitForFixedUpdate ( ) ;
    
    // replacement of yield return WaitUntil
    await UniTask . WaitUntil ( ( ) => isActive == false ) ;

    // special helper of WaitUntil
    await UniTask . WaitUntilValueChanged ( this , x => x . isActive ) ;

    // You can await IEnumerator coroutines
    await FooCoroutineEnumerator ( ) ;

    // You can await a standard task
    await Task . Run ( ( ) => 100 ) ;

    // Multithreading, run on ThreadPool under this code
    await UniTask . SwitchToThreadPool ( ) ;

    /* work on ThreadPool */

    // return to MainThread(same as `ObserveOnMainThread` in UniRx)
    await UniTask . SwitchToMainThread ( ) ;

    // get async webrequest
    async UniTask < string > GetTextAsync ( UnityWebRequest req )
    {
        var op = await req . SendWebRequest ( ) ;
        return op . downloadHandler . text ;
    }

    var task1 = GetTextAsync ( UnityWebRequest . Get ( \"http://goog*l*e*.com\" ) ) ;
    var task2 = GetTextAsync ( UnityWebRequest . Get ( \"http://*bi*ng.co*m\" ) ) ;
    var task3 = GetTextAsync ( UnityWebRequest . Get ( \"http://yahoo***.com\" ) ) ;

    // concurrent async-wait and get results easily by tuple syntax
    var ( google , bing , yahoo ) = await UniTask . WhenAll ( task1 , task2 , task3 ) ;

    // shorthand of WhenAll, tuple can await directly
    var ( google2 , bing2 , yahoo2 ) = await ( task1 , task2 , task3 ) ;

    // return async-value.(or you can use ` UniTask `(no result), ` UniTask Void`(fire and forget)).
    return ( asset as TextAsset ) ? . text ?? throw new InvalidOperationException ( \"Asset not found\" ) ;
} 

UniTask和异步的基础知识

UniTask功能依赖于C#7.0(类似任务的自定义ASYNC方法构建器功能),因此所需的Unity版本是在Unity 2018.3之后,支持的官方最低版本是Unity 2018.4.13f1

为什么需要使用UniTask (自定义任务样本对象)?因为任务太重,与Unity线程(单线程)不匹配。 UniTask不使用线程和同步context/executionContext,因为Unity的发动机层自动调度了Unity的异步对象。它实现了更快和降低的分配,并且与统一完全集成在一起。

您可以等待AsyncOperationResourceRequestAssetBundleRequestAssetBundleCreateRequestUnityWebRequestAsyncOperationAsyncGPUReadbackRequest ,iEnumerator,ienumerator, IEnumerator和其他using Cysharp.Threading.Tasks;

UniTask提供了三种扩展方法的模式。

 * await asyncOperation ;
* . WithCancellation ( CancellationToken ) ;
* . To UniTask ( IProgress , PlayerLoopTiming , CancellationToken ) ;

WithCancellation是一个简单的To UniTask的版本,均为返回UniTask 。有关取消的详细信息,请参见:取消和异常处理部分。

注意:等待直接从playerloop的本机时间返回,但与指定的playerlooptiming一起返回了cancellation和UniTask 。有关定时的详细信息,请参见:PlayerLoop部分。

注意:AssetBundLereQuest具有assetallAssets ,默认值等待返回asset 。如果要获得allAssets ,则可以使用AwaitForAllAssets()方法。

UniTask的类型可以使用UniTask .WhenAll whenall, UniTask .WhenAnyUniTask .WhenEach它们就像Task.WhenAll / Task.WhenAny当时,返回类型更有用。它们返回值元组,因此您可以解构每个结果并传递多种类型。

UniTaskVoid LoadManyAsync()
{
// parallel load.
var (a, b, c) = await UniTask .WhenAll(
LoadAsSprite(\”foo\”),
LoadAsSprite(\”bar\”),
LoadAsSprite(\”baz\”));
}

async UniTask <Sprite> LoadAsSprite(string path)
{
var resource = await Resources.LoadAsync<Sprite>(path);
return (resource as Sprite);
}\”>

 public async UniTask Void LoadManyAsync ( )
{
    // parallel load.
    var ( a , b , c ) = await UniTask . WhenAll (
        LoadAsSprite ( \"foo\" ) ,
        LoadAsSprite ( \"bar\" ) ,
        LoadAsSprite ( \"baz\" ) ) ;
}

async UniTask < Sprite > LoadAsSprite ( string path )
{
    var resource = await Resources . LoadAsync < Sprite > ( path ) ;
    return ( resource as Sprite ) ;
}

如果要将回调转换为UniTask ,则可以使用UniTask CompletionSource<T>这是TaskCompletionSource<T>轻量级版本。

UniTask CompletionSource()
{
var utcs = new UniTask CompletionSource<int>();

// when complete, call utcs.TrySetResult();
// when failed, call utcs.TrySetException();
// when cancel, call utcs.TrySetCanceled();

return utcs.Task; //return UniTask <int>
}\”>

 public UniTask < int > WrapBy UniTask CompletionSource ( )
{
    var utcs = new UniTask CompletionSource < int > ( ) ;

    // when complete, call utcs.TrySetResult();
    // when failed, call utcs.TrySetException();
    // when cancel, call utcs.TrySetCanceled();

    return utcs . Task ; //return UniTask <int>
}

您可以转换任务 – > UniTask : As UniTaskUniTask > UniTask <AsyncUnit>AsAsyncUnit UniTaskUniTask <T> – > UniTaskAs UniTaskUniTask <T> – > UniTask的转换成本是免费的。

如果要将异步转换为coroutine,则可以使用.ToCoroutine() ,如果您只允许使用Coroutine系统,这很有用。

UniTask不能等待两次。这与.NET标准2.1中引入的Valuetask/ivaluetasksource相似。

不应在Valuetask实例上执行以下操作:

  • 多次等待实例。
  • 多次致电ASTAKS。
  • 使用.result或.getawaiter()。getResult()操作尚未完成或多次使用它们。
  • 使用这些技术中的多种消耗实例。

如果您执行上述任何操作,则结果是不确定的。

UniTask.DelayFrame(10);
await task;
await task; // NG, throws Exception\”>

 var task = UniTask . DelayFrame ( 10 ) ;
await task ;
await task ; // NG, throws Exception

存储到类字段,您可以使用UniTask .Lazy支持多次调用。 .Preserve()允许多个呼叫(内部缓存结果)。当功能范围中有多个调用时,这很有用。

同样, UniTask CompletionSource可以等待多次,并等待许多呼叫者。

取消和例外处理

某些UniTask工厂方法具有CancellationToken cancellationToken = default参数。同样,某些统一的异步操作具有WithCancellation(CancellationToken)To UniTask (..., CancellationToken cancellation = default)扩展方法的扩展方法。

您可以通过标准CancellationTokenSourceCancellationToken传递给参数。

 var cts = new CancellationTokenSource ( ) ;

cancelButton . onClick . AddListener ( ( ) =>
{
    cts . Cancel ( ) ;
} ) ;

await UnityWebRequest . Get ( \"http://google.***co.jp\" ) . SendWebRequest ( ) . WithCancellation ( cts . Token ) ;

await UniTask . DelayFrame ( 1000 , cancellationToken : cts . Token ) ;

可以通过CancellationTokenSource或Monobehaviour的扩展方法GetCancellationTokenOnDestroy创建取消token。

 // this CancellationToken lifecycle is same as GameObject.
await UniTask . DelayFrame ( 1000 , cancellationToken : this . GetCancellationTokenOnDestroy ( ) ) ;

对于传播取消,所有异步方法都建议在最终参数中接受CancellationToken cancellationToken ,然后将CancellationToken从根到结束。

UniTask FooAsync(CancellationToken cancellationToken)
{
await BarAsync(cancellationToken);
}

async UniTask BarAsync(CancellationToken cancellationToken)
{
await UniTask .Delay(TimeSpan.FromSeconds(3), cancellationToken);
}\”>

 await FooAsync ( this . GetCancellationTokenOnDestroy ( ) ) ;

// ---

async UniTask FooAsync ( CancellationToken cancellationToken )
{
    await BarAsync ( cancellationToken ) ;
}

async UniTask BarAsync ( CancellationToken cancellationToken )
{
    await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) , cancellationToken ) ;
}

CancellationToken表示异步的生命周期。您可以持有自己的生命周期,而代替默认的comcellationTokenEndestroy。

 public class MyBehaviour : MonoBehaviour
{
    CancellationTokenSource disableCancellation = new CancellationTokenSource ( ) ;
    CancellationTokenSource destroyCancellation = new CancellationTokenSource ( ) ;

    private void OnEnable ( )
    {
        if ( disableCancellation != null )
        {
            disableCancellation . Dispose ( ) ;
        }
        disableCancellation = new CancellationTokenSource ( ) ;
    }

    private void OnDisable ( )
    {
        disableCancellation . Cancel ( ) ;
    }

    private void OnDestroy ( )
    {
        destroyCancellation . Cancel ( ) ;
        destroyCancellation . Dispose ( ) ;
    }
}

在Unity 2022.2之后,Unity在monobehaviour.destroycancellationtoken和application.exitCancellationToken中添加了concellationToken。

当检测到取消时,所有方法都会抛出OperationCanceledException并在上游传播。如果不使用异步方法处理异常(不限于OperationCanceledException ),则最终将其传播到UniTask Scheduler.UnobservedTaskException 。未接收的例外的默认行为是将日志写为例外。可以使用UniTask Scheduler.UnobservedExceptionWriteLogType更改日志级别。unobservedExceptionWritElogType。如果要使用自定义行为,请将操作设置为UniTask Scheduler.UnobservedTaskException.

而且OperationCanceledException是一个特殊的例外,在UnobservedTaskException时,这被默默地忽略。

如果您想在异步UniTask方法中取消行为,请手动扔OperationCanceledException

UniTask<int> FooAsync()
{
await UniTask .Yield();
throw new OperationCanceledException();
}\”>

 public async UniTask < int > FooAsync ( )
{
    await UniTask . Yield ( ) ;
    throw new OperationCanceledException ( ) ;
}

如果您处理异常,但想忽略(传播到全局取消处理),请使用异常过滤器。

UniTask<int> BarAsync()
{
try
{
var x = await FooAsync();
return x * 2;
}
catch (Exception ex) when (!(ex is OperationCanceledException)) // when (ex is not OperationCanceledException) at C# 9.0
{
return -1;
}
}\”>

 public async UniTask < int > BarAsync ( )
{
    try
    {
        var x = await FooAsync ( ) ;
        return x * 2 ;
    }
    catch ( Exception ex ) when ( ! ( ex is OperationCanceledException ) ) // when (ex is not OperationCanceledException) at C# 9.0
    {
        return - 1 ;
    }
}

投掷/捕获OperationCanceledException略微沉重,因此,如果性能是一个问题,请使用UniTask .SuppressCancellationThrow ,以避免使用操作CanceLedException Throw。它返回(bool IsCanceled, T Result)而不是投掷。

UniTask.DelayFrame(10, cancellationToken: cts.Token).SuppressCancellationThrow();
if (isCanceled)
{
// …
}\”>

 var ( isCanceled , _ ) = await UniTask . DelayFrame ( 10 , cancellationToken : cts . Token ) . SuppressCancellationThrow ( ) ;
if ( isCanceled )
{
    // ...
}

注意:仅当您直接呼叫到最源方法时,只会抑制投掷。否则,返回值将被转换,但是整个管道不会抑制投掷。

某些使用Unity播放器循环的功能,例如UniTask .YieldUniTask .Delay等,确定了播放器循环上的comcellationToken状态。这意味着它不会在CancellationToken射击后立即取消。

如果您想更改此行为,则立即取消将其设置为参数,将cancelImmediately为comment。

 await UniTask . Yield ( cancellationToken , cancelImmediately : true ) ;

注意: cancelImmediately设置为True并检测立即取消的设置比默认行为更为昂贵。这是因为它使用CancellationToken.Register ;它比在播放器循环上检查取消token的重量更重。

超时处理

超时是取消的变体。您可以通过CancellationTokenSouce.CancelAfterSlim(TimeSpan)设置超时,然后将comcellationToken传递到异步方法。

 var cts = new CancellationTokenSource ( ) ;
cts . CancelAfterSlim ( TimeSpan . FromSeconds ( 5 ) ) ; // 5sec timeout.

try
{
    await UnityWebRequest . Get ( \"http://**fo*o\" ) . SendWebRequest ( ) . WithCancellation ( cts . Token ) ;
}
catch ( OperationCanceledException ex )
{
    if ( ex . CancellationToken == cts . Token )
    {
        UnityEngine . Debug . Log ( \"Timeout\" ) ;
    }
}

CancellationTokenSouce.CancelAfter是标准API。但是,在统一上,您不应该使用它,因为它取决于线程计时器。 CancelAfterSlim是UniTask的扩展方法,它使用PlayerLoop。

如果要将超时与其他取消源一起使用,请使用CancellationTokenSource.CreateLinkedTokenSource

 var cancelToken = new CancellationTokenSource ( ) ;
cancelButton . onClick . AddListener ( ( ) =>
{
    cancelToken . Cancel ( ) ; // cancel from button click.
} ) ;

var timeoutToken = new CancellationTokenSource ( ) ;
timeoutToken . CancelAfterSlim ( TimeSpan . FromSeconds ( 5 ) ) ; // 5sec timeout.

try
{
    // combine token
    var linkedTokenSource = CancellationTokenSource . CreateLinkedTokenSource ( cancelToken . Token , timeoutToken . Token ) ;

    await UnityWebRequest . Get ( \"http://**fo*o\" ) . SendWebRequest ( ) . WithCancellation ( linkedTokenSource . Token ) ;
}
catch ( OperationCanceledException ex )
{
    if ( timeoutToken . IsCancellationRequested )
    {
        UnityEngine . Debug . Log ( \"Timeout.\" ) ;
    }
    else if ( cancelToken . IsCancellationRequested )
    {
        UnityEngine . Debug . Log ( \"Cancel clicked.\" ) ;
    }
}

优化以减少compellationTokenSource的分配,以进行按呼叫异步方法的超时,您可以使用UniTask的TimeoutController

UniTask FooAsync()
{
try
{
// you can pass timeoutController.Timeout(TimeSpan) to cancellationToken.
await UnityWebRequest.Get(\”http://**fo*o\”).SendWebRequest()
.WithCancellation(timeoutController.Timeout(TimeSpan.FromSeconds(5)));
timeoutController.Reset(); // call Reset(Stop timeout timer and ready for reuse) when succeed.
}
catch (OperationCanceledException ex)
{
if (timeoutController.IsTimeout())
{
UnityEngine.Debug.Log(\”timeout\”);
}
}
}\”>

 TimeoutController timeoutController = new TimeoutController ( ) ; // setup to field for reuse.

async UniTask FooAsync ( )
{
    try
    {
        // you can pass timeoutController.Timeout(TimeSpan) to cancellationToken.
        await UnityWebRequest . Get ( \"http://**fo*o\" ) . SendWebRequest ( )
            . WithCancellation ( timeoutController . Timeout ( TimeSpan . FromSeconds ( 5 ) ) ) ;
        timeoutController . Reset ( ) ; // call Reset(Stop timeout timer and ready for reuse) when succeed.
    }
    catch ( OperationCanceledException ex )
    {
        if ( timeoutController . IsTimeout ( ) )
        {
            UnityEngine . Debug . Log ( \"timeout\" ) ;
        }
    }
}

如果要将超时与其他取消源一起使用,请使用new TimeoutController(CancellationToken)

 TimeoutController timeoutController ;
CancellationTokenSource clickCancelSource ;

void Start ( )
{
    this . clickCancelSource = new CancellationTokenSource ( ) ;
    this . timeoutController = new TimeoutController ( clickCancelSource ) ;
}

注意: UniTask具有.Timeout.TimeoutWithoutException方法,但是,如果可能的话,请勿使用这些方法,请通过CancellationToken 。因为.Timeout .Timeout表示超时时忽略结果。如果将CancellationToken传递给该方法,它将从任务内部起作用,因此可以停止运行任务。

进步

统一的某些异步操作必须To UniTask (IProgress<float> progress = null, ...)扩展方法。

 var progress = Progress . Create < float > ( x => Debug . Log ( x ) ) ;

var request = await UnityWebRequest . Get ( \"http://google.***co.jp\" )
    . SendWebRequest ( )
    . To UniTask ( progress : progress ) ;

您不应使用标准的new System.Progress<T> 。使用Cysharp.Threading.Tasks.Progress 。该进度工厂有两种方法, CreateCreateOnlyValueChanged 。仅当进度值更改时, CreateOnlyValue

下载源码

通过命令行克隆项目:

git clone https://github.com/Cysharp/UniTask.git

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 UniTask https://www.zuozi.net/34256.html

options resolver
上一篇: options resolver
assh
下一篇: assh
常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务