Swift按顺序执行异步任务

时间:2017-08-14 20:34:05

标签: ios swift asynchronous afnetworking

我需要在我的应用上执行一些异步的网络任务。假设我需要从服务器获取3个资源,称之为ABC。假设我必须先获取资源A,然后才能获取BC。有时,我想首先获取B,有时首先获取C

现在,我只是有一个长链关闭,如下所示:

func fetchA() {
  AFNetworking.get(completionHandler: {
    self.fetchB()
    self.fetchC()
  })
}

现在可以使用,但明显的限制是我已经将执行顺序硬编码到fetchA的完成处理程序中。现在,假设我只想在fetchC完成该完成处理程序后fetchB,我必须更改fetchB的实现...

基本上,我想知道是否有一些神奇的方法可以做到:

let orderedAsync = [fetchA, fetchB, fetchC]
orderedAsync.executeInOrder()

其中fetchAfetchBfetchC都是异步函数,但fetchBfetchA完成后才会执行,依此类推。谢谢!

3 个答案:

答案 0 :(得分:3)

您可以使用与DispatchQueueDispatchGroup.enter()DispatchGroup.leave()混合的序列DispatchGroup.modify(),这将确保一次只运行一个执行块。

let serialQueue = DispatchQueue(label: "serialQueue")
let group = DispatchGroup()
group.enter()
serialQueue.async{  //call this whenever you need to add a new work item to your queue
    fetchA{
        //in the completion handler call
        group.leave()
    }
}
serialQueue.async{
    group.wait()
    group.enter()
    fetchB{
        //in the completion handler call
        group.leave()
    }
}
serialQueue.async{
    group.wait()
    group.enter()
    fetchC{
        group.leave()
    }
}

或者,如果您被允许使用第三方库,请使用PromiseKit,这使得处理和特别链接异步方法比GCD提供的方法更容易。有关详细信息,请参阅official GitHub页面。 您可以使用Promise中的完成处理程序包装异步方法,并将它们链接在一起,如下所示:

Promise.wrap(fetchA(completion:$0)).then{ valueA->Promise<typeOfValueB> in
    return Promise.wrap(fetchB(completion:$0)
}.then{ valueB in

}.catch{ error in
    //handle error
}

此外,所有错误都通过您的承诺传播。

答案 1 :(得分:0)

您可以结合使用dispatchGroup和dispatchSemaphore依次执行异步代码块。

  • DispatchGroup将保留输入和离开通知所有任务完成的时间。
  • 值为1的DispatchSemaphore将确保仅执行一个任务块

样本 其中fetchA,fetchB,fetchC是带有闭包的函数(完成处理程序)

public class PlayerMovement : MonoBehaviour
{

    public float speed = 12f;
    public float gravity = -0.05f;
    public float jumpHeight = 4f;

    public Transform groundCheck;
    public float groundDistance = 0.4f;
    public LayerMask groundMask;

    Vector3 velocity;
    Vector3 move;
    private bool isGrounded;

    public CharacterController controller;

    void FixedUpdate()
    {
        isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);

        if (isGrounded && velocity.y < 0)
        {
            velocity.y = -2f;
        }

        float x = Input.GetAxis("Horizontal");
        float z = Input.GetAxis("Vertical");

        Vector3 move = transform.right * x + transform.forward * z;
        move.Normalize();

        controller.Move(Time.deltaTime * speed * move);

        if (Input.GetButtonDown("Jump") && isGrounded)
        {
            velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
        }

        velocity.y += gravity * Time.deltaTime;

        controller.Move(velocity * Time.deltaTime);
    }
}

答案 2 :(得分:-1)

您所描述的已经是串行队列的默认行为:

let orderedAsync = [fetchA, fetchB, fetchC]
let queue = DispatchQueue(label: "fetchQueue")

for task in orderedAsync{
    queue.async(execute: task)
}

print("all enqueued")

“所有入队”将立即打印,每个任务将等待上一个任务完成才开始。

仅供参考,如果您将attributes: .concurrent添加到DispatchQueue初始化中,则不能保证它们按顺序执行。但是即使这样,当您希望事情按顺序执行时,也可以使用.barrier标志。

换句话说,这也将满足您的要求:

let queue = DispatchQueue(label: "fetchQueue", attributes: .concurrent)

for task in orderedAsync{
    queue.async(flags: .barrier, execute: task)
}