账号密码登录
微信安全登录
微信扫描二维码登录

登录后绑定QQ、微信即可实现信息互通

手机验证码登录
找回密码返回
邮箱找回 手机找回
注册账号返回
其他登录方式
分享
  • 收藏
    X
    JS事件循环问题
    19
    0

    我的需求:

     我的需求可以简单描述为,对一个大文件进行分片切割上传。我实现的思路为,
     对一个大文件,按照设定的chunksSize切分为N = file.size/chunkSize块,
     然后循环创建N个读流读取每个分片的内容,然后发起N个http.request的Post请求去上传文件。
     

    代码如下
    (说明: upload函数用来根据分块的个数n,计算每块起始标志位和终止标识位,并调用senddataPromise函数对每片进行操作)

    function  upload(username,filepath,file_id,filelength,n,alreadychunks,chunkSize) {
        return new Promise(function (resolve,reject) {
                var start = 0,end = 0;
                var promiseall = [];
                for (let curindex = 0;curindex < n;curindex++) {
                    if(filelength - start <= chunkSize) {
                        end  =  filelength - 1;
                    }else {
                        end = start+chunkSize - 1; // 因读取时包含start和end位
                    }
                    if(alreadychunks.indexOf(curindex) == -1) {
                        let options = {
                            flags: 'r',
                            highWaterMark: chunkSize,
                            start: start,
                            end: end
                        };
                        promiseall.push(senddataPromise(filepath,options,username,file_id,curindex,end-start+1));
                    }
                    start = end + 1;
                }
                let timer = setInterval(() => {
                    if(promiseall.length == n) {
                        clearInterval(timer);
                        Promise.all(promiseall).then(values=>{
                            console.log(values);
                            console.log("all done");
                            resolve(true)
                        }).catch(err => {
                            console.log(err);
                            reject(err);
                        })
                    }
                },500)
        })
    }

    senddataPromise函数是对第i块分片创建读流读取内容,并调用doapost函数发送到后端

    function senddataPromise(path,options,username,summary,curindex,length) {
        return new Promise(function (resolve,reject) {
            let readSteam = fs.createReadStream(path,options);
            readSteam.on("data",(chunk) => {
                console.log("第"+curindex+"块 JSON开始")
                let chunkjson = JSON.stringify(chunk);
                console.log("第"+curindex+"块 JSON结束")
                let tempcell = {
                    data: chunkjson,
                    n: curindex,
                    file_id: summary,
                    username: username,
                    length: length
                };
                chunk = null;
                chunkjson = null;
                doapost(tempcell).then(values=>{
                    resolve(values)
                }).catch(err=>{
                    reject(err);
                });
            })
        })
    }

    doapost函数发起post请求发送分片数据

    function  doapost(data) {
        return new Promise(function (resolve,reject) {
            let i = data.n;
            console.log("第"+i+"份请求准备发出")
            let contents = queryString.stringify(data);
            data = null;
            let options = {
                host: "localhost",
                path: "/nodepost/",
                port: 8000,
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                    'Content-Length': contents.length
                }
            };
            let req = http.request(options, function (res) {
                console.log("第"+i+"份请求返回数据")
                res.on("data", function (chunk) {
                    console.log(chunk.toString());
                });
                res.on("end", function (d) {
                    resolve("end");
                });
                res.on("error", function (e) {
                    reject(e);
                })
            });
            req.write(contents);
            req.end();
            contents = null;
            console.log("第"+i+"份请求已发出")
        })
    }
    

    我的问题:

      按照正常的思路,因为读取文件内容为异步操作,后面发送请求也为异步操作,所以
      也就是说会出现对于n个分片,读取数据已经读取了p片,并且已经有q(**q < p**)片
      已经完成上传完成返回数据的情况,但是现在问题是,***发现并没有分片上传完返回数据的
      情况出现,都是在n个分片读取完成后,才开始统一执行分片内容上传操作***
      图片如下:(由于图片无法上传,我把程序运输出拷贝一下)
    { 
     kind: 'upload',
    username: 'moran999',
    filepath: 'F:/my_upload_test/NowTest.pdf',
    file_id: '-196987878-472217752177633040957425519',
    alreadychunks: [],
    chunkSize: 1048576,
    n: 9 }
    第0块 JSON开始
    第0块 JSON结束
    第0份请求准备发出
    第0份请求已发出
    第1块 JSON开始
    第1块 JSON结束
    第1份请求准备发出
    第1份请求已发出
    第2块 JSON开始
    第2块 JSON结束
    第2份请求准备发出
    第2份请求已发出
    第3块 JSON开始
    第3块 JSON结束
    第3份请求准备发出
    第3份请求已发出
    第5块 JSON开始
    第5块 JSON结束
    第5份请求准备发出
    第5份请求已发出
    第4块 JSON开始
    第4块 JSON结束
    第4份请求准备发出
    第4份请求已发出
    第6块 JSON开始
    第6块 JSON结束
    第6份请求准备发出
    第6份请求已发出
    第8块 JSON开始
    第8块 JSON结束
    第8份请求准备发出
    第8份请求已发出
    第7块 JSON开始
    第7块 JSON结束
    第7份请求准备发出
    第7份请求已发出
    第8份请求返回数据
    moran999
    第4份请求返回数据
    moran999
    第6份请求返回数据
    moran999
    第1份请求返回数据
    moran999
    第2份请求返回数据
    moran999
    第0份请求返回数据
    moran999
    第3份请求返回数据
    moran999
    第7份请求返回数据
    moran999
    第5份请求返回数据
    moran999
    [ 'end', 'end', 'end', 'end', 'end', 'end', 'end', 'end', 'end' ]
    all done
    
      可以看到其POST数据的发出并不是和读流无关的,即任何一个POST都不会发出,
      直到到所有的读流读取完数据,想问一下各位码友是什么原因尼??因为正常
      理解下当第i个读流读的时候,前面已经读取完内容的读流完全可以进行post操作
      了啊,但实际上并没有。
    
      之所以会问这个问题是因为当我输入的文件比较大时,他执行到《第12块 JSON开始时,
      就内存溢出了》,而如果程序是post不用等待所有的读流读完时,当有一部分post执行完之后,其对应的数据就被回收了,释放相应的内存,就不会出现内存溢出了。
      
      
      
    
    0
    打赏
    收藏
    点击回答
        全部回答
    • 0
    • iii!iii 普通会员 1楼

      JavaScript的事件循环是用于处理DOM元素的事件处理程序,以及事件的执行顺序。在JavaScript中,我们通常使用addEventListener方法为DOM元素添加事件监听器,当事件发生时,事件监听器会调用JavaScript函数来处理事件。

      但是,事件循环的问题主要有以下几个:

      1. 事件循环机制:JavaScript的事件循环是一个异步的事件循环机制,这意味着JavaScript会阻塞在事件循环中,等待事件处理程序完成。这样,当用户进行操作,比如点击按钮,JavaScript就会立即执行事件处理程序,但不会等待事件处理程序完成。

      2. 回调问题:在JavaScript中,事件处理程序通常返回一个函数,这个函数会在事件结束后执行。如果事件处理程序执行了很长时间,或者没有返回函数,那么JavaScript就会一直等待事件处理程序完成,这就会导致页面卡死。

      3. 事件冒泡问题:在JavaScript中,事件的处理程序会按照事件的冒泡顺序依次执行。这意味着,如果事件在一个元素上触发,那么事件处理程序会首先处理那个元素上的事件,然后处理父元素上的事件,最后处理子元素上的事件。

      解决这些问题的方法主要是使用Promise、async/await等异步编程技术,以及使用事件委托和事件循环机制。

    更多回答
    扫一扫访问手机版
    • 回到顶部
    • 回到顶部