美文网首页react-native
React Native 踩坑日记(十一) —— JS 中的同步

React Native 踩坑日记(十一) —— JS 中的同步

作者: 黑羽肃霜 | 来源:发表于2018-03-21 21:59 被阅读2116次

    综述

    网上有很多关于 Promise,async await 的使用教程了,在这里不赘述,介绍两个相关教程
    async_await详解(彻底摆脱回调地狱)
    阮一峰的 es6教程之 Promise

    这里做的笔记分享,主要是针对第一个视频的。旨在记录如何在一个async函数中实现同步和异步

    实例

    调用的代码模板(部分为伪代码)

    templateFunction = async (param_1, param_2) => {
      try {
        let host = await getHostAsyncFunc();
        let session = await getSessionAsyncFunc(host);
        let requestURL = await gererateReqAsyncFunc(session,host);
      } catch ( error => {
        console.log(error);
      });
    }
    
    //...
    
    function getHostAsyncFunc() {
      return new Promise ((resolve,reject) => {
          if (goRight) {
            resolve(true);
          } else {
            reject('error running');
          }
      });
    }
    

    这里来做一个简单的说明。

    因为网络请求是一个天生的异步调用情况,因此以此为例。

    通常我们做一个网络请求的时候,会先去获取多个相应的参数。
    而在React Native 开发中,异步调用(不单单包括网络请求),获取参数是非常常见的情况。例如,原生开放接口供 RN 调用,返回的结果通常就是异步返回的。

    因为 Promise 对象通过thencatch捕获返回的结果

    • Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数
    • Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。then方法的第一个参数是resolved状态的回调函数

    当异步返回需要等待上一步执行(实质上是一种同步需求)时,就产生了嵌套

    嵌套,其实我们很熟悉了,包括OC中用blockGCD做嵌套,JS中使用then,
    最后代码都会写成形如下面这样的 N 级嵌套,俗称造火箭

    then (
      then (
        then (
          then ()
        )
      )
    )
    

    Async await 的使用

    视频里说的很详细了,这里就不做赘述。简而言之,从上面那段实例代码中可以看出:

    • await 后头可以跟一个Promise对象,用来接收他的 resolve(或者可以看做then)的执行结果

      await 是一个有兼容性的语法,后头并不仅仅跟 Promise对象,普通的函数调用结果也可以接收

    • catch 接收的是所有 Promise 中产生的错误。通俗的理解,只要await后头执行的东西产生了错误,那么就会立即停下,进入catch.

      这也带来了一个问题,也许我 await 了N 个异步函数,但是catch的时候我不知道究竟是哪个错误执行到了。这就要求我们在各个异步函数中将reject的内容要标注好,便于调试

    同步和异步

    啰嗦了半天,终于到了最需要注意的地方了。

    需要声明的是:我们这里说的同步和异步,严格意义上说,是这个 async 函数内部的执行是同步的还是异步的。因为从外来看,整个async函数一定是一个异步函数,毋庸置疑。

    同步

    同步就是上面说的那种情况了,我的后一个函数需要依赖前一个函数的执行结果,那么就需要等待他的返回。

    异步

    举一个开发中的实例来说明:

    • 现在我有 N张图,后台返回给我的是一个可以查询到这些图片 URL 地址的 key_id 数组:
      [image_key1,image_key2,image_key3,image_key4]
    • 有一个异步函数 getImageURL(imageKeysArray:Array), 可以通过上面的key_id返回对应的url数组,
    • 我等待拿到所有的 URL 组成的数组 [image_url1,image_url2,image_url3,image_url4]后,返回给下一个异步函数showImage(urlArray:Array),异步加载这些图片

    这里就涉及到了这个问题:
    key => url 的过程,是异步的,但是我必须知道你所有的 key 都转化完成的时机,然后才能返回生成的 url 数组。这时候又是同步的

    代码说明:

    showImages = async (imageKeyID_array:Array, sucCallback:Function, failCallback:Function) => {
      try {
        let promiseArray = []
        for (let keyID of imageKeyID_array) {
          promiseArray.push(getImgURLAsyncFunc(keyID));
        }
        
        let urls = await Promise.all(promiseArray);
        let imageComponentsArray = [];
        urls.map((url,index) => {
          imageComponentsArray.push(<Image source={uri:url} key={index}/>)
        })
        sucCallback(imageComponentsArray);
      } catch (e){
        if (failCallback != null)
          failCallback(e);
      }
    }
    
    
    function getImgURLAsyncFunc() {
      return new Promise ((resolve,reject) => {
          if (goRight) {
            resolve(true);
          } else {
            reject('error running');
          }
      });
    }
    

    这里,将所有获取图片 URL 的函数返回的Promise 对象放在一个数组里,让他一次去执行。
    let urls = await Promise.all(promiseArray);这句会等待所有的异步操作都执行完毕.

    最后,试想下,如果用gcd来做,是不是要用到dispatch_barrier ?


    2018.4.4 补充
    使用 Promise进行异步和异步搭配使用
    参考资料

    当你创建一个 Promise 实例的时候,任务就已经开始执行了,比如下面代码:

    function fn(resolve, reject) {
      console.log('hello');
      // ...
    }
    
    console.log('before');
    const promiseGetter = () => new Promise(fn); // fn 没有立即执行
    console.log('after');
    

    const promiseGetter = () => new Promise(fn) 叫做 PromiseGetter

    你会在控制台里依次看到 before、hello 和 after。这是因为你传递给 Promise 的函数 fn 是被立即执行的

    我们现在需要执行的需求是这样的:
    这里先声明下iOS系统下上传图片的限制:

    假设并发上传9张大图,单次上传的数据量有限制,如果9张图同时并发,就会造成后面还没开始传的图片超时。

    这里我如果使用Promise.all()来做上传,在 ios 系统下是会有问题的。
    所以,我们实际的需求为:

    每次传3张图,这次上传的3张是并发的。等到3张传完后,再继续3张,重复前面的步骤。
    用同步异步的来解释,先做3张异步,都传完的时间点,是同步的。抓包看到的效果如下:

    示意图
    // 这里简化下代码,也就是通过一个循环,做了9次操作,把9个 promiseGetter 函数放进这个数组
    let dentryID_array = [];//用来存储图片识别码的数组
    uploadImgPromiseGetter.push(()=>{return _uploadImg(uploadImgParams)}
    
    if (Platform.OS === 'ios') {
        // ios 需要作一个判断,根据机型,一般单次取3个图片作异步上传,完成后再取3张
        for (let index = 0; index < 3; index ++) {
            let tempArray = uploadImgPromiseGetter.splice(0,3);//如果 数组是[],那么做了 splice 还是返回[]
            let uploadImgPromises = [];
            tempArray.map(promiseGetter => {
                if (promiseGetter) {
                    uploadImgPromises.push(promiseGetter());
                }
            });
    
            if (uploadImgPromises.length > 0) { // 判断这个数组中是否存了 promiseGetter 函数
                let dentryID = await Promise.all(uploadImgPromises);
                dentryID_array.push(...dentryID);
            }
        }
    } else {
        let uploadImgPromises = [];
        uploadImgPromiseGetter.map(promiseGetter => {
            if (promiseGetter) {
                uploadImgPromises.push(promiseGetter());
            }
        });
        let dentryID = await Promise.all(uploadImgPromises);
        dentryID_array.push(...dentryID);
    }
    

    相关文章

      网友评论

        本文标题:React Native 踩坑日记(十一) —— JS 中的同步

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