美文网首页
从同步到异步 - iOS转RN最需转变的思路

从同步到异步 - iOS转RN最需转变的思路

作者: waterwind | 来源:发表于2017-04-10 00:54 被阅读130次

    说是iOS转React Native,不如说是iOS转JavaScript最需要转变的思路。

    本质上来说JavaScript是单线程语言;同时,可能因为它是为网络而生,需要高频度地向服务器读取数据;再者,可能因为它所在的客户端——浏览器——的执行效率并不高;因此,它采取了大量的异步化操作。例如,要装载图片,需要有一个异步读取图片的过程;要读取文件/数据库,也必须异步读取;可能最令iOS程序员发指的是,如果要使用原生的iOS模块,那么必然会要用异取读取。

    所以,原来简单的iOS程序,就会变得支离破碎。就拿读文件来说,在iOS程序中,你只需要在一个模块中按顺序写下来就好:

    1)获得文件路径
    2)打开文件,读取文件内容(若读取时间长,顶多来一个UI转圈界面展示)
    3)使用文件内容

    转到RN中呢,你需要

    1)获得文件路径
    2)开一个子线程去读文件
    3)准备一个回调(callback)函数,等待子线程读取后再返回

    看起来好像没有什么区别,但是,由于JS是单线程函数,你还不能在主线程中单纯等待返回。真实的情况是,主线程执行完了,才会去执行子线程。因此,一个简单的读取文件的过程,就不得不写成好像从服务器异步读取数据一样麻烦。

    单是读取文件也罢了,若是你写了一个iOS原生模块,是各种工具函数的集合,然后会在RN模块中反复调用。那么,你就需要在等待一个函数返回之后,再调用另一个函数,于是,在多次调用之后,你发现已陷入到传说中的“回调地狱”!

    说实话,这是我从iOS编程转向RN的过程中,最不习惯的一道坎!比起编程环境搭建,比起各种模块的调用方式,这个思路上的转变最让人闹心。

    最后,还是找到一些相对简单的解决方法。以下这篇文章给出了最有用的一种方法:
    http://blog.csdn.net/kunshan_shenbin/article/details/40425143

    简单说,就是用特定的语法方式,让编写代码的过程,变得不再大括号套小括号。以下是这种方法使用的简单流程。

    1.在文件头引入Generator、以及next控制函数的工具性代码

    // 当前的 Generator
    let activeGenerator;
    
    // 处理 g.next() 功能
    function gNext() {
        return function(err, data) {
        if(err) {
            throw err;
        }
        //  g.next(),并把回调函数的结果作为参数传递给 yield
        activeGenerator.next(data)
      }
    }
    
    // 控制工具
    function gQueue(generatorFunc) {
      activeGenerator= generatorFunc(gNext());
      activeGenerator.next();
    }
    

    2.异步调用的时候使用这种语法开头

      // 该语句实际产生了一个generator函数,并定义了其中的next操作
       gQueue(function * flow(next) {
    

    “flow”这个名字是随便取的,这里表示它是一个流程控制工具,该名字不会被再次调用。接下来,我们在gQueue中,就能大胆地像写同步语法一样,使用异步调用了:

            let result1 = yield (callBack => {
                someModule.someFunc1(var1, var2..., callBack);
            })(next);
    
            // 以下语句会在result1获得值后继续执行
            let result2 = yield (callBack => {
                someModule.someFunc2(result, var2..., callBack);
            })(next);
    

    实际上,由于generator函数的特性,每次.next()调用,都会来到新的一条yield语句。然后,该函数所在线程会暂停,开始调用yield语句中的匿名函数。在匿名函数中,我们放置了自己所想要调用的异步操作:someModule.someFunc。

    之后的方式非常巧妙,注意,上述匿名函数所使用的形参callBack,在调用的时候,被实参 next 替代。因此,callBack返回,实际上是调用了 next 函数。而根据最前面的工具定义,next 对应的是 gNext() 函数【JS的语法这个地方有点绕,需要多看几遍】,因此,callBack函数中,定义的第一个参数 error 用来描述返回结果是否有误,而第二个参数data,则被当成返回值传给了yield。那么,我们的 result 就获得了这个data值。

    更巧妙的是,当 g.next(data)除了返回data值,实质上还执行了一次.next操作。那么,generator函数所有线程将继续执行,这样就保证了后续语句立刻会在返回后运行。实现了看似“同步连续执行”的效果。

    如果对以上机制仍感模糊,阮一峰老师的这篇文章值得推荐:Generator 函数的语法

    3.回调函数callBack的特殊规则

    根据以上机制,要正确地使用该方法,必须对我们使用的callBack使用特定的返回机制。callBack的第一个参数 error 必须用来描述返回结果是否有误,而第二个参数data则用于返回真正需要的数据。假如你返回的数据有多个,那么必须打包成一个数据,然后在返回值里再做反向解析。以下是这一callBack机制的正确使用方式:

        function someFunction(var1, var2, callback) {
            // some operation,最后得到两个值要返回
            let result1 = ...;
            let result2 = ...;
            callback(null, [result1, result2]);
        }
    

    最后的语句中,偷懒把错误值定成了null,待返回的两个值,包装成一个数组作为data返回过去了。那么后面调用时,获得这个两个值,就必须分别从结果中以数组方式解析。

    4.其它

    这篇文章只是描述了异步调用的一个最大障碍。相关的问题还有 setTimeout 函数的应用,以及针对异步模块调用的监听等技术方案,将来有空再来细述。

    相关文章

      网友评论

          本文标题:从同步到异步 - iOS转RN最需转变的思路

          本文链接:https://www.haomeiwen.com/subject/mkjjattx.html