项目中使用Block频次很高,经常用到回调闭包多级的嵌套,所以最近对Promise比较感兴趣。
很巧,今天技术群里有人抛出一个问题。一个非常常见的需求,如果是你,你会怎么做?会不会使用一种更优雅的方式?
PM:我们要实现一个下载音频的功能,下载前需要做这些处理。
- 如果网络异常,直接提示网络异常并退出下载;
- 如果是4G网络,需要弹窗提醒用户选择是否继续下载,用户点击下载后进入下载流程,取消后直接退出下载;
- 如果 Wi-Fi 直接进入下载;
- 下载时需要获取下载地址,获取到下载地址后,进入下载;
- 下载完成需要刷新UI;
梳理完逻辑后是这样的:
这个好办,不一会就写完了,结果是这样的
1 | - (void)downloadCallBack:(void(^)(BOOL isSuccess))callback { |
这种写法主要有回调地狱和代码冗余问题,后期维护困难。
何为优雅?如何优雅?
Promise!
1 | - (void)downloadForPromiseCallBack:(void(^)(BOOL isSuccess))callback { |
Promise背景简介
Promise最初被提出是在 E语言中, 它是基于并列/并行处理设计的一种编程语言。
Promise更多应用于 JavaScript,主要是为了解决 JavaScript 的 Callback Hell(回调地狱) 问题。 简单来说就是, JS 中很异步函数都是用 callback 的形式返回结果,而开发者经常会连续的使用这些异步调用,最后就导致回调嵌套的层级越来越深,严重影响代码的结构和可读性。Promise 的出现,就是用来解决 Callback Hell 这个问题的(用线性调用的接口来封装回调嵌套)。
Promise应用
- GCD异步下载图片
当我们需要异步下载一张图片并赋给Image 用GCD实现,请看下面这一坨OC:
1 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); |
用 Promise 中的 dispatch_promise :
1 | dispatch_promise(^{ |
dispatch_promise做了什么事情?看源码不难发现dispatch_promise中为我们建好了队列并在then中返回了MainThread。
- 用catch处理Error
让我们再看看文章开头的那个例子
1 | - (void)downloadForPromiseCallBack:(void(^)(BOOL isSuccess))callback { |
在之前需要在检查是否可以下载,请求下载地址和开始下载中分别对Error进行处理
catch是怎样做的?在传递promise的链中,一旦中间任何一环产生了错误,都会传递到catch去执行Error Handler。
- always
1 | [self login].then(^(){ |
在所有的then和catch执行完还会执行always,根据业务选择是否实现。
- 用PMKWhen处理多异步任务作为前置任务
比如有一个这样的需求,需要从服务端下载两张图片,然后比较图片的size
1 | PMKWhen(@[[self downloadFirstPicture], [self downloadSecondPicture]]).then(^(NSArray *results){ |
PMKWhen传入数组,数组中存放promise,当数组中所有promise执行完毕才会执行then。
1 | PMKWhen(@{@"firstPicture":[self downloadFirstPicture], |
PMKWhen还可以传入存放prmise的字典。
一直在追求如何更有效率的开展工作
项目中使用block频次之高,PromiseKit无疑是最优雅的处理CallBack hell的方式之一,学习了PromiseKit源码思想感触很深。异步问题,回调嵌套,回调地狱一直都是项目中的痛点,通过PromiseKit可以相对优雅的解决,使项目逻辑更加清晰。