ThisAV 发表于 2022-1-24 07:22:36

怎么用 Promise 让所有 GM_xmlhttpRequest 完成再进行下一步操作?

本帖最后由 ThisAV 于 2022-1-25 14:04 编辑

https://www.xiurenji.net/XiuRen/5409.html

//获取图片生成
                  let currentPageID=location.href.match(/(\d+)(?:_\d+)?.html/),
                        pageMax=$('.page>a:last').text()=='下页'?$('.page>a:last').prev().text():$('.page>a:last').text();
                  let ImgList=[];

                  async function getImgUrl(array){
                        const ret = [];
                        const executing = [];
                        return new Promise(async (resolve, reject) => {
                            try {
                              for(let currentPage=0;currentPage<(+pageMax);currentPage++) {
                                        let PageID = currentPage==0 ? `/${currentPageID}.html` : `/${currentPageID}_${currentPage}.html`,
                                          PageUrl = location.pathname.replace(/\/[^/.]+\.html/i, PageID);

                                    await $.get(PageUrl, (res,s,e)=>{
                                        $('.content img', res).map(function(i, e){
                                          ImgList.push(this.src);
                                          //console.log(PageID, i, e, this.src)
                                        });
                                    })
                              }
                            } catch(e){
                              console.error(e);
                            } finally {
                              Promise.all(ret);
                            }
                        })
                  }
                  getImgUrl();


      let padZero = (n) => (num) => {
            if (typeof num === 'number') num += '';
            return num.padStart(n, '0');
      }


    function requestUrlBlob(url) {
      return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                url: url,
                method: 'get',
                responseType : 'blob',
                onload :res => resolve(res.response),
                onerror : e => reject(e)
            });
      })
    }

    async function downloadAndPackFile(zipPack, url, filename, extname) {
      if (!extname) {
            const urlExt = url.split('.').pop();
            extname = urlExt ? `.${urlExt}` : '';
      }

      console.log(`正在下载`, url, filename);
      await requestUrlBlob(url).then((blob) => {
            console.log(`下载完成`, url, blob, filename);
            zipPack.file(`${filename}${extname}`, blob);
      });
    }

    async function startDownload(ImgList, zipName) { // zipName = 压缩包名字
      const zipPack = new window.JSZip();
      const urlObjList = ImgList.map((url, idx) =>({url, filename: padZero(3)(idx),}));

      console.log('开始下载');
      await asyncPool(5, urlObjList, ({ url, filename }) => {
            downloadAndPackFile(zipPack, url, filename);
      });

      await zipPack.generateAsync({ type: 'blob' }, function(metadata) {
            let progress = metadata.percent / 100;
            console.log(metadata, progress);
      }).then(
            function (blob) {
                // 1) generate the zip file
                window.saveAs(blob, `${zipName}.zip`); // 2) trigger the download
            },
            function (err) {
                console.error('[批量下载] ERR', err);
            }
      );
      console.log('资源下载完成,开始打包资源', zipName);
    }

    async function asyncPool(poolLimit, array, iteratorFn) { //通过任务池限制并发数量,任务池 poolLimit=任务上限,array=任务对象,iteratorFn=任务内容
      const ret = [];
      const executing = [];
      for (const item of array) {
            const task = Promise.resolve().then((y, n) => {
                iteratorFn(item, array)
            }); //等待处理完成
            ret.push(task);

            console.log(task);
            if (poolLimit <= array.length) {
                const e = task.then(() => executing.splice(executing.indexOf(e), 1));
                executing.push(e);
                if (executing.length >= poolLimit) {
                  await Promise.race(executing);
                }
            }
      }
      return Promise.all(ret);
    }
      

对 Promise 不太熟悉,如上面的地址

startDownload 函数里

asyncPool 负责下载图片和打包

zipPack.generateAsync 执行下载动作

但是 zipPack.generateAsync没有等待 asyncPool 的 GM_xhr 完成就执行了下载动作,导致下载了一个空包
该怎么调整代码执行?


cxxjackie 发表于 2022-1-24 07:22:37

ThisAV 发表于 2022-1-24 15:59
感谢大佬回帖,是我的描述有问题,现有的代码就是这个操作方法

startDownload 函数里


感觉你对async/await的理解有问题,虽然强行异步后返回值变成了Promise,但代码实际还是同步的,这个downloadAndPackFile函数问题很大,return GM_xhr肯定得不到想要的结果,应该自己用Promise封装后return这个Promise(参考我给的download函数),asyncPool做的事跟我的downloadAll函数类似。

ThisAV 发表于 2022-1-24 07:32:20

PS: 因为图片地址会进行 301 跳转,fetch 会受到 cors 影响

李恒道 发表于 2022-1-24 09:42:07

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
是想用promise.all函数么

李恒道 发表于 2022-1-24 10:50:17

看了一下,Gm_xhr不可以用await哦
他没有返回promise
可以自己封装一个
https://bbs.tampermonkey.net.cn/thread-883-1-1.html

cxxjackie 发表于 2022-1-24 11:22:55

给你写个简单示例:
function download(url) {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
            url: url,
            method: 'get',
            responseType : 'blob',
            onload: res => resolve(res.response),
            onerror: e => reject(e)
      });
    });
}
function downloadAll(urlList) {
    const task = [];
    for (let url of urlList) {
      task.push(download(url));
    }
    return Promise.all(task);
}
要等待全部下载完成,await downloadAll()即可。

李恒道 发表于 2022-1-24 12:00:56

cxxjackie 发表于 2022-1-24 11:22
给你写个简单示例:

要等待全部下载完成,await downloadAll()即可。

大佬严谨!好久没看到cxxjackie哥哥了

ThisAV 发表于 2022-1-24 14:58:27

李恒道 发表于 2022-1-24 10:50
看了一下,Gm_xhr不可以用await哦
他没有返回promise
可以自己封装一个


意思是我只能用递归的方法去满足我的需求?

ThisAV 发表于 2022-1-24 15:59:01

cxxjackie 发表于 2022-1-24 11:22
给你写个简单示例:

要等待全部下载完成,await downloadAll()即可。

感谢大佬回帖,是我的描述有问题,现有的代码就是这个操作方法

startDownload 函数里

asyncPool 负责下载图片和打包

zipPack.generateAsync 执行下载动作

但是 zipPack.generateAsync没有等待 asyncPool 的 GM_xhr 完成就执行了

该怎么优化这个?

李恒道 发表于 2022-1-24 17:18:53

ThisAV 发表于 2022-1-24 14:58
意思是我只能用递归的方法去满足我的需求?

不用递归呀,
直接包装GM_xhr返回一个promise
然后全部塞到一个数组里
promise.all函数等待全部返回成功
进行下一步处理
页: [1] 2
查看完整版本: 怎么用 Promise 让所有 GM_xmlhttpRequest 完成再进行下一步操作?