上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
12下一页
返回列表 发新帖

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

[复制链接]
  • TA的每日心情

    2022-4-13 08:58
  • 签到天数: 14 天

    [LV.3]偶尔看看II

    3

    主题

    61

    帖子

    48

    积分

    初级工程师

    Rank: 4

    积分
    48
    发表于 2022-1-24 07:22:36 | 显示全部楼层 | 阅读模式
    15油猫币
    本帖最后由 ThisAV 于 2022-1-25 14:04 编辑

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

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

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

    13.                                     await $.get(PageUrl, (res,s,e)=>{
    14.                                         $('.content img', res).map(function(i, e){
    15.                                             ImgList.push(this.src);
    16.                                             //console.log(PageID, i, e, this.src)
    17.                                         });
    18.                                     })
    19.                                 }
    20.                             } catch(e){
    21.                                 console.error(e);
    22.                             } finally {
    23.                                 Promise.all(ret);
    24.                             }
    25.                         })
    26.                     }
    27.                     getImgUrl();


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


    32.     function requestUrlBlob(url) {
    33.         return new Promise((resolve, reject) => {
    34.             GM_xmlhttpRequest({
    35.                 url: url,
    36.                 method: 'get',
    37.                 responseType : 'blob',
    38.                 onload :  res => resolve(res.response),
    39.                 onerror : e => reject(e)
    40.             });
    41.         })
    42.     }

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

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

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

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

    61.         await zipPack.generateAsync({ type: 'blob' }, function(metadata) {
    62.             let progress = metadata.percent / 100;
    63.             console.log(metadata, progress);
    64.         }).then(
    65.             function (blob) {
    66.                 // 1) generate the zip file
    67.                 window.saveAs(blob, `${zipName}.zip`); // 2) trigger the download
    68.             },
    69.             function (err) {
    70.                 console.error('[批量下载] ERR', err);
    71.             }
    72.         );
    73.         console.log('资源下载完成,开始打包资源', zipName);
    74.     }

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

    83.             console.log(task);
    84.             if (poolLimit <= array.length) {
    85.                 const e = task.then(() => executing.splice(executing.indexOf(e), 1));
    86.                 executing.push(e);
    87.                 if (executing.length >= poolLimit) {
    88.                     await Promise.race(executing);
    89.                 }
    90.             }
    91.         }
    92.         return Promise.all(ret);
    93.     }
    94.         
    复制代码


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

    startDownload 函数里

    asyncPool 负责下载图片和打包

    zipPack.generateAsync 执行下载动作

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


    最佳答案

    查看完整内容

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

    [LV.1]初来乍到

    10

    主题

    315

    帖子

    402

    积分

    荣誉开发者

    Rank: 10Rank: 10Rank: 10

    积分
    402

    活跃会员热心会员突出贡献三好学生猫咪币纪念章中秋纪念章国庆纪念章

    发表于 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函数类似。
    回复

    使用道具 举报

  • TA的每日心情

    2022-4-13 08:58
  • 签到天数: 14 天

    [LV.3]偶尔看看II

    3

    主题

    61

    帖子

    48

    积分

    初级工程师

    Rank: 4

    积分
    48
    发表于 2022-1-24 07:32:20 | 显示全部楼层
    PS: 因为图片地址会进行 301 跳转,fetch 会受到 cors 影响
    回复

    使用道具 举报

  • TA的每日心情
    无聊
    2022-4-18 01:37
  • 签到天数: 30 天

    [LV.5]常住居民I

    302

    主题

    2582

    帖子

    2650

    积分

    荣誉开发者

    非物质文化遗产社会摇传承人

    Rank: 10Rank: 10Rank: 10

    积分
    2650

    猫咪币纪念章

    发表于 2022-1-24 09:42:07 | 显示全部楼层
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

  • TA的每日心情
    无聊
    2022-4-18 01:37
  • 签到天数: 30 天

    [LV.5]常住居民I

    302

    主题

    2582

    帖子

    2650

    积分

    荣誉开发者

    非物质文化遗产社会摇传承人

    Rank: 10Rank: 10Rank: 10

    积分
    2650

    猫咪币纪念章

    发表于 2022-1-24 10:50:17 | 显示全部楼层
    看了一下,Gm_xhr不可以用await哦
    他没有返回promise
    可以自己封装一个
    https://bbs.tampermonkey.net.cn/thread-883-1-1.html
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2022-3-8 11:41
  • 签到天数: 2 天

    [LV.1]初来乍到

    10

    主题

    315

    帖子

    402

    积分

    荣誉开发者

    Rank: 10Rank: 10Rank: 10

    积分
    402

    活跃会员热心会员突出贡献三好学生猫咪币纪念章中秋纪念章国庆纪念章

    发表于 2022-1-24 11:22:55 | 显示全部楼层
    给你写个简单示例:
    1. function download(url) {
    2.     return new Promise((resolve, reject) => {
    3.         GM_xmlhttpRequest({
    4.             url: url,
    5.             method: 'get',
    6.             responseType : 'blob',
    7.             onload: res => resolve(res.response),
    8.             onerror: e => reject(e)
    9.         });
    10.     });
    11. }
    12. function downloadAll(urlList) {
    13.     const task = [];
    14.     for (let url of urlList) {
    15.         task.push(download(url));
    16.     }
    17.     return Promise.all(task);
    18. }
    复制代码

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

    使用道具 举报

  • TA的每日心情
    无聊
    2022-4-18 01:37
  • 签到天数: 30 天

    [LV.5]常住居民I

    302

    主题

    2582

    帖子

    2650

    积分

    荣誉开发者

    非物质文化遗产社会摇传承人

    Rank: 10Rank: 10Rank: 10

    积分
    2650

    猫咪币纪念章

    发表于 2022-1-24 12:00:56 | 显示全部楼层
    cxxjackie 发表于 2022-1-24 11:22
    给你写个简单示例:

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

    大佬严谨!好久没看到cxxjackie哥哥了
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

  • TA的每日心情

    2022-4-13 08:58
  • 签到天数: 14 天

    [LV.3]偶尔看看II

    3

    主题

    61

    帖子

    48

    积分

    初级工程师

    Rank: 4

    积分
    48
    发表于 2022-1-24 14:58:27 | 显示全部楼层
    李恒道 发表于 2022-1-24 10:50
    看了一下,Gm_xhr不可以用await哦
    他没有返回promise
    可以自己封装一个

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

    使用道具 举报

  • TA的每日心情

    2022-4-13 08:58
  • 签到天数: 14 天

    [LV.3]偶尔看看II

    3

    主题

    61

    帖子

    48

    积分

    初级工程师

    Rank: 4

    积分
    48
    发表于 2022-1-24 15:59:01 | 显示全部楼层
    cxxjackie 发表于 2022-1-24 11:22
    给你写个简单示例:

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

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

    startDownload 函数里

    asyncPool 负责下载图片和打包

    zipPack.generateAsync 执行下载动作

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

    该怎么优化这个?
    回复

    使用道具 举报

  • TA的每日心情
    无聊
    2022-4-18 01:37
  • 签到天数: 30 天

    [LV.5]常住居民I

    302

    主题

    2582

    帖子

    2650

    积分

    荣誉开发者

    非物质文化遗产社会摇传承人

    Rank: 10Rank: 10Rank: 10

    积分
    2650

    猫咪币纪念章

    发表于 2022-1-24 17:18:53 | 显示全部楼层
    ThisAV 发表于 2022-1-24 14:58
    意思是我只能用递归的方法去满足我的需求?

    不用递归呀,
    直接包装GM_xhr返回一个promise
    然后全部塞到一个数组里
    promise.all函数等待全部返回成功
    进行下一步处理
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

    发表回复

    本版积分规则

    快速回复 返回顶部 返回列表