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

综述

网上有很多关于 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);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 异步编程对JavaScript语言太重要。Javascript语言的执行环境是“单线程”的,如果没有异步编程,根本...
    呼呼哥阅读 7,334评论 5 22
  • async 函数 含义 ES2017 标准引入了 async 函数,使得异步操作变得更加方便。 async 函数是...
    huilegezai阅读 1,278评论 0 6
  • Promise 对象 Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函...
    neromous阅读 8,729评论 1 56
  • 弄懂js异步 讲异步之前,我们必须掌握一个基础知识-event-loop。 我们知道JavaScript的一大特点...
    DCbryant阅读 2,750评论 0 5
  • 看完《大叔的爱》这部剧后,超级心疼部长大叔。虽然是一部bl剧,但全程特别清新脱俗。少女心爆棚的大叔,贤惠的小鲜肉,...
    尘余阅读 734评论 0 5