我需要在我的应用上执行一些异步的网络任务。假设我需要从服务器获取3个资源,称之为A
,B
和C
。假设我必须先获取资源A
,然后才能获取B
或C
。有时,我想首先获取B
,有时首先获取C
。
现在,我只是有一个长链关闭,如下所示:
func fetchA() {
AFNetworking.get(completionHandler: {
self.fetchB()
self.fetchC()
})
}
现在可以使用,但明显的限制是我已经将执行顺序硬编码到fetchA
的完成处理程序中。现在,假设我只想在fetchC
完成该完成处理程序后fetchB
,我必须更改fetchB
的实现...
基本上,我想知道是否有一些神奇的方法可以做到:
let orderedAsync = [fetchA, fetchB, fetchC]
orderedAsync.executeInOrder()
其中fetchA
,fetchB
和fetchC
都是异步函数,但fetchB
在fetchA
完成后才会执行,依此类推。谢谢!
答案 0 :(得分:3)
您可以使用与DispatchQueue
,DispatchGroup.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依次执行异步代码块。
样本 其中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)
}