iOS实现await-async的js引擎下的语法

回调的本质

函数指针

  • 抛开所有的语言, 回调的本质是函数指针, 因为操作系统是基于C语言编写的自然的回调的原理就是基于C的函数指针, 这一点不多作解释
  • 在后来的编译器实现中, 出现了局部函数, lambda, 闭包...等它们的基本原理和函数指针一样, 绑定了函数地址, 基于这种原理用户在编写函数时异步的回调不用再分开到 新的函数中去书写
void func(int data){
    ++data;     // __code_user_2
}



typedef void(* Func)(int*);
void OS_API(Func fun){
    // 异步操作
    ..
    
    fun(200);
    
    ...
}

int main(int args, char** argv){
    OS_API(func);       // __code_user_1;
}

场景是调用系统的api, 系统会在内部通过传递的函数, 将有关数据回调(data)出来, 这样的方式是最原始的方式, 其中code_user_1 和 code_user_2这2个用户级别的代码在书写时迫于标准C的语法被分开


void OS_API(void(^cbk)(int data)){
    
    // ...
    
    cbk(3);
    
    // ...
    
}

int main(int args, char** argv){
    OS_API(^void(int data){
        // 回调的代码
    });
}

这里以iOS来举例, 调用的代码回调的代码是在一起书写, 可维护性比纯C的回调要好很多, 其它语言(JS, Java等下的lambda也是一样的, 但随着开发以及业务增大, 回调中的代码越来越多, 会变得更不可维护, 所以傻瓜式的JS出现了 async-await的书写, 使语法更简洁

const myFun2 = function(val, time) {
    return new Promise((resolve, reject) =>{
        setTimeout(()=>{
            resolve(val)
        }, time)
    })
}

const demo_3 = async function() {
    let a = await myFun2(3, 5000)
    console.log(a)
    let b = await myFun2(4, 10000)
    console.log(b)
    let c =  await myFun2(5, 15000)
    console.log(c)
    let d = a + b +c  
    console.log(d)
}

demo_3()

这里 js引擎内部提供了async, await, 内部实现原理其实很简单

  • demo_3被调用时, js引擎会开启新的线程, 利用锁机制实现 await的同步等待
  • 这使得js的同学们写代码不再关心回调嵌套的恶心代码

iOS下的网络请求

// HTTP是封装了AF
[HTTP GETSuccess:^(id  _Nullable success) {
        [MOSAdvanceLoadM register_key:@"MOSLocalLifeVC" data:success[@"data"]];
    } failure:^(HTTP_ERROR * _Nonnull failure) {

    } url:API->dg.ym.service_lst args:nil header:nil HUD:nil];

这种写法已经写噎死了, 本人不才, 实现了简单的await语法, 这个过程是基于C语言下的macro扩展来的


/** await 专用 */
__attribute__((objc_subclassing_restricted)) @interface HTTP_RES : NSObject

/** 200 成功, 其他失败 */
@property (nonatomic) NSInteger code;

/** 若code 为200,则 res = @{@"data":res, @"msg":success[@"message"]};
    若code 不为200, 则 res = HTTP_ERROR对象
 */
@property (nonatomic,strong) id _Nonnull res;

@property (nonatomic,strong,readonly) HTTP_ERROR* err;
@end







#define Async(__type, __var)                                    \
        "".length;                                              \
        dispatch_async(dispatch_get_global_queue(0, 0), ^{      \
        __unused __block __type __var = __var;                  \
        __unused __block __type __strong * __tmp_var = &__var;  \
        __block pthread_mutex_t __m_lock;                       \
        pthread_mutex_init(&__m_lock, NULL);                    \
        __block pthread_cond_t __cond;                          \
        pthread_cond_init(&__cond, NULL);                       \
        __unused id __place


#define Await                                                   \
        (0);                                                    \
        dispatch_async(dispatch_get_main_queue(), ^{            \



#define Signal()                                                \
        "".length;                                              \
        pthread_mutex_lock(&__m_lock);                          \
        pthread_mutex_unlock(&__m_lock);                        \
        pthread_cond_signal(&__cond);                           \


#define Catch                                                   \
        "".length;                                              \
        });                                                     \
        pthread_mutex_lock(&__m_lock);                          \
        pthread_cond_wait(&__cond, &__m_lock);                  \
        pthread_mutex_unlock(&__m_lock);                        \
        dispatch_async(dispatch_get_main_queue(), ^{



#define Over()                                                  \
        "".length;                                              \
        @Signal();                                              \
        });                                                     \
        pthread_mutex_lock(&__m_lock);                          \
        pthread_cond_wait(&__cond, &__m_lock);                  \
        pthread_mutex_unlock(&__m_lock);                        \
        pthread_mutex_destroy(&__m_lock);                       \
        pthread_cond_destroy(&__cond);                          \
        })



/**
 填充值 是HTTP_RES,
 hud外部自己处理
 */
extern void __http_req(NSString*            _Nonnull    url,
                       NSDictionary*        _Nullable   req_dic,
                       id __strong *        _Nonnull    result,
                       pthread_mutex_t*     _Nonnull    m_lock,
                       pthread_cond_t*      _Nonnull    cond,
                       NSInteger type);

#define http_req(_url,  _req_dic,   _type)                              \
    __http_req( (_url),                                                 \
                (_req_dic),                                             \
                (__tmp_var),                                            \
                (&__m_lock),                                            \
                (&__cond),                                              \
                (_type))                                                \
                ;



// 要求要有 HUD函数(当前工程必须导入 LB_DEV.h), __data是 HTTP_RES
#define HTTP_ERR_REMIND(__data)                                         \
    assign_code{                                                        \
        if([__data isKindOfClass:NSClassFromString(@"HTTP_RES")] &&     \
            ((HTTP_RES*)__data).code != 200){                           \
            __auto_type hud = HUD(((HTTP_ERROR*)(__data.res)).err);     \
            HUD_HIDE(hud, 1);                                           \
        }                                                               \
    }

对应函数的实现


@implementation HTTP_RES
- (HTTP_ERROR *)err{
    if(self.code == 200)
        return nil;

    return (HTTP_ERROR*)self.res;
}
@end




void __http_req(NSString*           _Nonnull    url,
                NSDictionary*       _Nullable   req_dic,
                id __strong *       _Nonnull    result,
                pthread_mutex_t*    _Nonnull    m_lock,
                pthread_cond_t*     _Nonnull    cond,
                NSInteger type){
    typedef void(^ Res_cbk)(NSURLSessionDataTask* _Nonnull, id _Nullable);
    typedef void(^ Err_cbk)(NSURLSessionDataTask* _Nullable task, NSError* _Nonnull);

    Res_cbk suc_cbk = ^void(NSURLSessionDataTask* task, id responseObject){
        
        id resultData = [HTTP objcWithData:responseObject];
        {BLOG(task.originalRequest.URL);}
        {BLOG([resultData mj_JSONString]);}
        if(res_code(resultData) == 200){
            if(result){
                HTTP_RES* tmp = [HTTP_RES new];
                tmp.code = 200;
                tmp.res = res_suc(resultData);
                *result = tmp;
            }
//          atomic_store((atomic_bool*)atomic_lock, true);

            pthread_mutex_lock(m_lock);
            pthread_mutex_unlock(m_lock);
            pthread_cond_signal(cond);
        }else{
            if(MOSUser.TOKEN.length && res_code(resultData) == 401){
                POST_NOTI(nil, PRO_NOTI_P->EXCEPT_LOGOUT);
//              atomic_store((atomic_bool*)atomic_lock, true);
                pthread_mutex_lock(m_lock);
                pthread_mutex_unlock(m_lock);
                pthread_cond_signal(cond);
                return;
            }

            if(result){
                __auto_type tmp = [HTTP_RES new];
                tmp.code = res_code(resultData);
                tmp.res = [HTTP_ERROR err:res_code(resultData)
                                         :resultData[@"message"]];
                *result = tmp;
            }
//          atomic_store((atomic_bool*)atomic_lock, true);
            pthread_mutex_lock(m_lock);
            pthread_mutex_unlock(m_lock);
            pthread_cond_signal(cond);
        }
    };


    Err_cbk err_cbk = ^void(NSURLSessionDataTask* task, NSError* err){
        {BLOG(task.originalRequest.URL);}
        {BLOG(err);}
        if(result){
            __auto_type tmp = [HTTP_RES new];
            tmp.code = -1;
            tmp.res = [HTTP_ERROR err:-1
                                     :err];
            *result = tmp;
        }

//      atomic_store((atomic_bool*)atomic_lock, true);
        pthread_mutex_lock(m_lock);
        pthread_mutex_unlock(m_lock);
        pthread_cond_signal(cond);
    };





    // get
    if(type == 0){
        [HTTP md5_dic:req_dic :&url];
        [HTTP.sessionMgr GET:url
                  parameters:nil
                     headers:nil
                    progress:nil
                     success:suc_cbk
                     failure:err_cbk];
    }

    if(type == 1){
        req_dic = [HTTP md5_dic:req_dic :nil];
        [HTTP.sessionMgr POST:url
                   parameters:req_dic
                      headers:nil
                     progress:nil
                      success:suc_cbk
                      failure:err_cbk];

    }
}

使用

// assign_code是一上空的宏
assign_code{
        @Async(id, result) = @Await http_req(API->dg.ym.service_lst, nil, 0) @Catch;


        NSLog(@"%@ %@", __tmp_var, result);

        HTTP_ERR_REMIND(result);

// 不要在这里面 使用 __tmp_var, 因为 *__tmp_var指向的是 nil, 直接使用 result
        @Over();
}

原理是用到系统的atomic锁(已废弃,使用的是mutex_lock)以及配合宏, 实现后的代码, 在单次网络请求的写法是基本和js是一样的, 这整个过程的实现也不作解释了, 有内核编程的思想在里面, 自己用, 这里记录一下
关于锁, 本来想使用 atomic, 但由于atomic在wait的时间段占用cpu资源, 本人想的解决方案时通过unix信号suspend来作pause的唤醒处理, 但在iOS环境中系统屏蔽了所有信号相关的函数, 所以改用了mutext_lock, 可能对于信号这方面本人目前在iOS系统层面上的知识体系还不够充分, 先放这里, 以后搞清楚了再改为atomic

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容