MAP ARG阵列以后要执行的承诺数组

发布于 2025-02-07 16:12:31 字数 2324 浏览 1 评论 0原文

我正在构建脚本的层次结构,其中所有级别的脚本都可以并行运行,但是每个级别都需要在下一个级别运行之前完成。

当我有一个固定的已知级别数量的级别时,我通过查询文档的脚本,在级别1中有一个工作,具有在ARG传递加载时返回承诺的函数,将该函数推到数组中,重复每个级别的函数,然后创建一个承诺。所有的链条用于生成的阵列:

function loadScript (script) {
  return new Promise(function(resolve, reject) {
    script.setAttribute("src", script.getAttribute("data-src"));
    script.onload = function() {
      resolve(script.src);
    };
    script.onerror = function() {
      reject(script.src);
    }
  });
}

function loadScripts() {
  const firstScripts = document.querySelectorAll( "script[data-type='lazy']"
    + "[data-order='1']" );
  const secondScripts = document.querySelectorAll( "script[data-type='lazy']"
    + "[data-order='2']" );

  let first = [];
  let second = [];

  firstScripts.forEach(function(element) {
    first.push(loadScript(element));
  });
  secondScripts.forEach(function(element) {
    second.push(loadScript(element));
  });

  Promise.all(first)
    .then(Promise.all(second))
     .then(function() {
       console.log('all scripts loaded');
     })
     .catch(function(script) {
       console.log(script + ' failed to load.');
     });
 }

我现在正在尝试将其抽象一点,因此它将与任意,未知的深度一起使用。这似乎是一个使用array.map的好地方;但是,问题是,它实际上是在执行承诺。

因此,我正在寻找的是将调用映射到loadscript的方法,以便以后可以打电话:

function loadScripts() {
  let maxDepth = 0;
  let hierarchy = [];

  while ( Boolean(document.querySelector( "script[data-type='lazy']"
    + "[data-order='" + maxDepth + "']" )) ) {

    hierarchy[ maxDepth ] = [ ...document.querySelectorAll(
      "script[data-type='lazy'][data-order='" + maxDepth + "']" ) ];

    maxDepth++;
  }

  hierarchy.forEach((scripts, level) => {
    // these loadScript calls should ... not be calls. They should be queued to
    // run later when walkHierarchy is called
    hierarchy[ level ] = scripts.map((element) => loadScript( element ) );
  });

  walkHierarchy = async function (arr) {
    for (const level of arr) await Promise.all(level);
  }

  walkHierarchy( hierarchy )
    .then(function() {
      console.log('all scripts loaded');
    })
    .catch(function(script) {
      console.log(script + ' failed to load.');
    });
}

另外,如果有一种方法可以采用WalkhierArchy逻辑并将其包含在eReach.foreach中,以便每个级别在进入下一个级别之前都可以完成。

I'm building up a hierarchy of scripts, where all the scripts at the same level can be run in parallel, but each level needs to complete before the next level runs.

I had this working when I had a fixed, known number of levels, by querying the document for the scripts at level 1, having a function that returns a promise when the arg passed loads, pushing that function onto an array, repeating for each level, and then creating a Promise.all chain for the resulting arrays:

function loadScript (script) {
  return new Promise(function(resolve, reject) {
    script.setAttribute("src", script.getAttribute("data-src"));
    script.onload = function() {
      resolve(script.src);
    };
    script.onerror = function() {
      reject(script.src);
    }
  });
}

function loadScripts() {
  const firstScripts = document.querySelectorAll( "script[data-type='lazy']"
    + "[data-order='1']" );
  const secondScripts = document.querySelectorAll( "script[data-type='lazy']"
    + "[data-order='2']" );

  let first = [];
  let second = [];

  firstScripts.forEach(function(element) {
    first.push(loadScript(element));
  });
  secondScripts.forEach(function(element) {
    second.push(loadScript(element));
  });

  Promise.all(first)
    .then(Promise.all(second))
     .then(function() {
       console.log('all scripts loaded');
     })
     .catch(function(script) {
       console.log(script + ' failed to load.');
     });
 }

I'm now trying to abstract it a bit, so it will work with an arbitrary, unknown depth. This seems like a fine place to use Array.map; however, the problem is, it's actually executing the Promise.

So, what I'm looking for is a way to map the calls to loadScript such that they can be called later:

function loadScripts() {
  let maxDepth = 0;
  let hierarchy = [];

  while ( Boolean(document.querySelector( "script[data-type='lazy']"
    + "[data-order='" + maxDepth + "']" )) ) {

    hierarchy[ maxDepth ] = [ ...document.querySelectorAll(
      "script[data-type='lazy'][data-order='" + maxDepth + "']" ) ];

    maxDepth++;
  }

  hierarchy.forEach((scripts, level) => {
    // these loadScript calls should ... not be calls. They should be queued to
    // run later when walkHierarchy is called
    hierarchy[ level ] = scripts.map((element) => loadScript( element ) );
  });

  walkHierarchy = async function (arr) {
    for (const level of arr) await Promise.all(level);
  }

  walkHierarchy( hierarchy )
    .then(function() {
      console.log('all scripts loaded');
    })
    .catch(function(script) {
      console.log(script + ' failed to load.');
    });
}

Alternatively, if there's a way to take the walkHierarchy logic and include it in the hierarchy.forEach, such that each level completed before moving on to the next, that would be fine, too.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

初心未许 2025-02-14 16:12:31

动态导入

也许您正在寻找 dynamigic import 它已经支持按需加载。

diy级别订单脚本加载程序

也许您不想使用import,或者在环境中不支持它。让我们以一个具体的例子,以便我们可以验证行为。我们从一堆无序的< scripts> -

<script data-type="lazy" data-src="a.js" data-order="1"></script>
<script data-type="lazy" data-src="b.js" data-order="1"></script>
<script data-type="lazy" data-src="bbbb.js" data-order="4"></script>
<script data-type="lazy" data-src="aa.js" data-order="2"></script>
<script data-type="lazy" data-src="bb.js" data-order="2"></script>
<script data-type="lazy" data-src="aaa.js" data-order="3"></script>
<script data-type="lazy" data-src="c.js" data-order="1"></script>
<script data-type="lazy" data-src="cc.js" data-order="2"></script>
<script data-type="lazy" data-src="d.js" data-order="1"></script>
<script data-type="lazy" data-src="bbb.js" data-order="3"></script>
<script data-type="lazy" data-src="aaaa.js" data-order="4"></script>

Graph

第一步是制作graph of (订购,脚本,脚本)阵列)地图 -

// script : html_script_element
// order : int
// graph : (order, script array) map
// makeGraph : script iterable -> graph
function makeGraph(scripts) {
  const g = new Map
  for (const s of scripts) {
    const order = Number(s.getAttribute("data-order"))
    if (!g.has(order)) g.set(order, [])
    g.get(order).push(s)
  }
  return g
}
makeGraph(document.querySelectorAll("script[data-type=lazy]"))
Map {
  1 => [<script>, <script>, <script>, <script>],
  4 => [<script>, <script>],
  2 => [<script>, <script>, <script>],
  3 => [<script>, <script>]
}

loadscripts

该图出现无关紧要。 loadscripts将递归遍历图形,递增order一对一。当图中找不到特定顺序时,我们知道是时候停止加载并退出 -

// loadScripts : (graph, int) -> (string array) promise
async function loadScripts(g, order = 1) {
  if (!g.has(order))
    return []
  else
    return [
      ...await Promise.all(g.get(order).map(loadScript)),
      ...await loadScripts(g, order + 1)
    ]
}

loadscript

我们可以在此处通过修改loadscript to 解决此处进行演示。脚本是否成功加载。我们还添加了一个console.log,因此我们可以看到哪个顺序loadScript现在in -

// loadScript : script -> string promise
function loadScript(script) {
  return new Promise(function(resolve, reject) {
    console.log("loading", script.getAttribute("data-src")) // ⚠️ for demo
    script.setAttribute("src", script.getAttribute("data-src"))
    script.onload = _ => resolve(script.src)
    script.onerror = _ => resolve(script.src) // ⚠️ resolve for demo only
  })
}

现在一起

loadScripts(makeGraph(document.querySelectorAll("script[data-type=lazy]")))
  .then(console.log)
  .catch(console.error)
loading a.js
loading b.js
loading c.js
loading d.js
loading aa.js
loading bb.js
loading cc.js
loading aaa.js
loading bbb.js
loading bbbb.js
loading aaaa.js
[
  "https://stacksnippets.net/a.js",
  "https://stacksnippets.net/b.js",
  "https://stacksnippets.net/c.js",
  "https://stacksnippets.net/d.js",
  "https://stacksnippets.net/aa.js",
  "https://stacksnippets.net/bb.js",
  "https://stacksnippets.net/cc.js",
  "https://stacksnippets.net/aaa.js",
  "https://stacksnippets.net/bbb.js",
  "https://stacksnippets.net/bbbb.js",
  "https://stacksnippets.net/aaaa.js"
]

demo

在您自己的浏览器中验证结果 -

function loadScript(script) {
  return new Promise(function(resolve, reject) {
    console.log("loading", script.getAttribute("data-src")) // ⚠️ demo only
    script.setAttribute("src", script.getAttribute("data-src"))
    script.onload = _ => resolve(script.src)
    script.onerror = _ => resolve(script.src) // ⚠️ demo only
  })
}

async function loadScripts(g, order = 1) {
  if (!g.has(order))
    return []
  else
    return [
      ...await Promise.all(g.get(order).map(loadScript)),
      ...await loadScripts(g, order + 1)
    ]
}

function makeGraph(scripts) {
  const g = new Map
  for (const s of scripts) {
    const order = Number(s.getAttribute("data-order"))
    if (!g.has(order)) g.set(order, [])
    g.get(order).push(s)
  }
  return g
}

loadScripts(makeGraph(document.querySelectorAll("script[data-type=lazy]")))
  .then(console.log)
  .catch(console.error)
.as-console-wrapper { min-height: 100%; top: 0; }
<script data-type="lazy" data-src="a.js" data-order="1"></script>
<script data-type="lazy" data-src="b.js" data-order="1"></script>
<script data-type="lazy" data-src="bbbb.js" data-order="4"></script>
<script data-type="lazy" data-src="aa.js" data-order="2"></script>
<script data-type="lazy" data-src="bb.js" data-order="2"></script>
<script data-type="lazy" data-src="aaa.js" data-order="3"></script>
<script data-type="lazy" data-src="c.js" data-order="1"></script>
<script data-type="lazy" data-src="cc.js" data-order="2"></script>
<script data-type="lazy" data-src="d.js" data-order="1"></script>
<script data-type="lazy" data-src="bbb.js" data-order="3"></script>
<script data-type="lazy" data-src="aaaa.js" data-order="4"></script>

dynamic import

Maybe you are looking for dynamic import which already supports on-demand loading.

DIY level-order script loader

Maybe you don't want to use import or it's not supported in your environment. Let's take a concrete example so we can verify the behaviour. We start with a bunch of unordered <scripts> -

<script data-type="lazy" data-src="a.js" data-order="1"></script>
<script data-type="lazy" data-src="b.js" data-order="1"></script>
<script data-type="lazy" data-src="bbbb.js" data-order="4"></script>
<script data-type="lazy" data-src="aa.js" data-order="2"></script>
<script data-type="lazy" data-src="bb.js" data-order="2"></script>
<script data-type="lazy" data-src="aaa.js" data-order="3"></script>
<script data-type="lazy" data-src="c.js" data-order="1"></script>
<script data-type="lazy" data-src="cc.js" data-order="2"></script>
<script data-type="lazy" data-src="d.js" data-order="1"></script>
<script data-type="lazy" data-src="bbb.js" data-order="3"></script>
<script data-type="lazy" data-src="aaaa.js" data-order="4"></script>

graph

The first step is to make a graph of (order, script array) map -

// script : html_script_element
// order : int
// graph : (order, script array) map
// makeGraph : script iterable -> graph
function makeGraph(scripts) {
  const g = new Map
  for (const s of scripts) {
    const order = Number(s.getAttribute("data-order"))
    if (!g.has(order)) g.set(order, [])
    g.get(order).push(s)
  }
  return g
}
makeGraph(document.querySelectorAll("script[data-type=lazy]"))
Map {
  1 => [<script>, <script>, <script>, <script>],
  4 => [<script>, <script>],
  2 => [<script>, <script>, <script>],
  3 => [<script>, <script>]
}

loadScripts

It doesn't matter that the graph appears unordered. loadScripts will recursively traverse the graph, incrementing order one-by-one. When a particular order is not found in the graph, we know it is time to stop loading and exit -

// loadScripts : (graph, int) -> (string array) promise
async function loadScripts(g, order = 1) {
  if (!g.has(order))
    return []
  else
    return [
      ...await Promise.all(g.get(order).map(loadScript)),
      ...await loadScripts(g, order + 1)
    ]
}

loadScript

We can demo this here by modifying loadScript to resolve whether the script loads successfully or not. We also added a console.log so we can see which order loadScript is called in -

// loadScript : script -> string promise
function loadScript(script) {
  return new Promise(function(resolve, reject) {
    console.log("loading", script.getAttribute("data-src")) // ⚠️ for demo
    script.setAttribute("src", script.getAttribute("data-src"))
    script.onload = _ => resolve(script.src)
    script.onerror = _ => resolve(script.src) // ⚠️ resolve for demo only
  })
}

all together now

loadScripts(makeGraph(document.querySelectorAll("script[data-type=lazy]")))
  .then(console.log)
  .catch(console.error)
loading a.js
loading b.js
loading c.js
loading d.js
loading aa.js
loading bb.js
loading cc.js
loading aaa.js
loading bbb.js
loading bbbb.js
loading aaaa.js
[
  "https://stacksnippets.net/a.js",
  "https://stacksnippets.net/b.js",
  "https://stacksnippets.net/c.js",
  "https://stacksnippets.net/d.js",
  "https://stacksnippets.net/aa.js",
  "https://stacksnippets.net/bb.js",
  "https://stacksnippets.net/cc.js",
  "https://stacksnippets.net/aaa.js",
  "https://stacksnippets.net/bbb.js",
  "https://stacksnippets.net/bbbb.js",
  "https://stacksnippets.net/aaaa.js"
]

demo

Verify the results in your own browser -

function loadScript(script) {
  return new Promise(function(resolve, reject) {
    console.log("loading", script.getAttribute("data-src")) // ⚠️ demo only
    script.setAttribute("src", script.getAttribute("data-src"))
    script.onload = _ => resolve(script.src)
    script.onerror = _ => resolve(script.src) // ⚠️ demo only
  })
}

async function loadScripts(g, order = 1) {
  if (!g.has(order))
    return []
  else
    return [
      ...await Promise.all(g.get(order).map(loadScript)),
      ...await loadScripts(g, order + 1)
    ]
}

function makeGraph(scripts) {
  const g = new Map
  for (const s of scripts) {
    const order = Number(s.getAttribute("data-order"))
    if (!g.has(order)) g.set(order, [])
    g.get(order).push(s)
  }
  return g
}

loadScripts(makeGraph(document.querySelectorAll("script[data-type=lazy]")))
  .then(console.log)
  .catch(console.error)
.as-console-wrapper { min-height: 100%; top: 0; }
<script data-type="lazy" data-src="a.js" data-order="1"></script>
<script data-type="lazy" data-src="b.js" data-order="1"></script>
<script data-type="lazy" data-src="bbbb.js" data-order="4"></script>
<script data-type="lazy" data-src="aa.js" data-order="2"></script>
<script data-type="lazy" data-src="bb.js" data-order="2"></script>
<script data-type="lazy" data-src="aaa.js" data-order="3"></script>
<script data-type="lazy" data-src="c.js" data-order="1"></script>
<script data-type="lazy" data-src="cc.js" data-order="2"></script>
<script data-type="lazy" data-src="d.js" data-order="1"></script>
<script data-type="lazy" data-src="bbb.js" data-order="3"></script>
<script data-type="lazy" data-src="aaaa.js" data-order="4"></script>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文