JavaScript的灵活调度承诺

分享于 

10分钟阅读

Web开发

  繁體

介绍

JavaScript中的承诺对于处理许多异步任务非常有用。 标准函数 Promise.prototype.then() 和 Promise.all() 分别用于调度按顺序执行和并行执行的任务。

但是,假设我们要执行许多异步任务,其中一些依赖于其他任务。 由于依赖关系,Promise.all() 不会工作,Promise.prototype.then() 将非常低效,因为我们将在不需要的时候执行任务。

在本文中,我们提出了一个简单的函数来处理相互依赖的JavaScript ( 例如 ) 网络的最佳调度情况。 异步任务的有向无环图和它们之间的依赖关系。

background: Promise.prototype.then() 和 Promise.all( )

JavaScript语言提供了内置机制,可以将多个异步任务组合成一个承诺。 我们将讨论其中的两个。

Promise.prototype.then( )

也许组合承诺最重要的方式是使用 Promise.prototype.then() [ reference ]。 它允许将一个承诺链接到另一个,这样就可以按顺序执行两个异步任务。

可以使用 then() 来形成连续执行的异步任务的长链。 一个输出的输出将作为输入输入到下一个。 以下是promise链接的示例:

// promise that resolves after t milliseconds// with no valuefunction sleep(t) {
 returnnew Promise((resolve, reject) => {
 setTimeout(() => {
 console.log("resolved", t);
 resolve();
 }, t);
 });
}
sleep(3000)
.then((value) => { return sleep(2000); })
.then((value) => { return sleep(1000); })
.then((value) => { console.log("done!"); });

在图片中,这就是 Promise.prototype.then()的用途:

Promise.prototype.then() runs many tasks sequentially.

Promise.all( )

另一种组合承诺的方法是使用 Promise.all() [ reference ]。 它允许从清单( 那么,iterable ) 中创建承诺的承诺

  • 一旦被拒绝的承诺被拒绝,就会被拒绝
  • 当所有提供的承诺都已经解决时,。

特别是,提供的所有承诺都代表并行运行的任务。 下面是 Promise.all()的使用示例:

// promise that resolves after t milliseconds// with no valuefunction sleep(t) {
 returnnew Promise((resolve, reject) => {
 setTimeout(() => {
 console.log("resolved", t);
 resolve();
 }, t);
 });
}
Promise.all([sleep(3000), sleep(2000), sleep(1000)])
.then((value) => { console.log("done!"); })

在图片中,这就是 Promise.all()的用途:

Promise.all() runs many tasks in parallel.

推广:任务的有向无环图

我们在本文中所写的函数推广了承诺( 使用 Promise.prototype.then() )的链接和同时执行的诺求( 使用 Promise.all() )。 它允许高效地执行基于任务的网络任务( 或者如果你想要),或者你需要的任务( 如任务的依赖关系图。 换句话说,我们编写的函数适合于 上面 类型的情况,但也适用于这里类情况:

A directed acyclic graph of promises.

函数参数

我们会打电话给 function promiseDAG() (DAG stands for directed acyclic graph)。 它将如下所示:

function promiseDAG(callbacks, dag) {
. . .
}

提供的参数包括:

  • callbacks: 函数列表,每个函数在调用时返回一个 promise
    这些是我们要执行的异步任务,它们是有向无圈图中的节点。
  • dag: 列表列表,指定任务之间的依赖关系
    这些指定有向环图( 他们决定将哪些参数提供给回调) 中的边。

如果提供了回调,那么 dag 应该是整数的一个列表,其中包含整数。 我说的是,我是一个依赖者。 比如 dag[i] 包含整数 ,那么有向环图就有一个边缘,该边来自于 task'我的任务是 '。

通过执行以下操作来执行显示的网络:

// each of these should return a promise that executes the taskfunction task0() {
 return.. .
}function task1(value0) {
 return.. .
}function task2(value0) {
 return.. .
}function task3(value1, value2) {
 return.. .
}function task4(value2) {
 return.. .
}var p = promiseDAG([task0, task1, task2, task3, task4], [[], [0], [0], [1,2], [2]]);

功能行为

调用 promiseDAG() 时,任何没有传入边的任务( 例如。 不依赖于其他任务的启动。 每当任务完成时,所有已经完成它的先决条件的任务都将立即启动。 向回调提供的参数它与解析的先决条件,与在 dag 中指定的相同。

注意:当一个JavaScript函数用多于它接受的参数调用时,多余的参数会被忽略。 如果你不知道taskA的值,那么你就可以在有向无环图中表示依赖关系,而不需要任何参数来写。

成功完成所有任务后,promiseDAG() 返回的承诺将得到解决。 返回的,的值是与 callbacks 相同的长度列表,包含任务的所有已经解析的值的返回值。

一旦任务失败,promiseDAG() 返回的承诺就会被拒绝。 错误将与失败任务被拒绝的相同。 将不再启动新任务( 尽管当前运行的任务将继续运行,因为无法取消挂起的承诺)。

函数的内部工作

让我们浏览一下 promiseDAG()的内部内容。 函数的结构如下:

function promiseDAG(callbacks, dag) {
 returnnew Promise((resolve, reject) => {
 var N = callbacks.length;
 var counts = dag.map((x) => x.length);
 // extra variables herefunction handleResolution(promise, i, value) {
. . .
 }
 function handleRejection(promise, i, error) {
. . .
 }
 // start all tasks that have no incoming arrowsfor(let i=0; i<N; ++i) {
 if(counts[i]> 0) {
 continue;
 }
 var promise = callbacks[i]();
 promise.then(
 (value) => { handleResolution(promise, i, value); },
 (error) => { handleRejection(promise, i, error); });
 }
 });
}

函数 handleResolution() 将 register 解析为承诺的值,并开始任何现在已经满足它的前提条件的承诺。

函数 handleRejection() 将简单地拒绝由 promiseDAG() 构造的承诺,传递错误的错误。

使用代码:示例

假设你运行一个网站,里面有一天的视频。 当用户访问站点时,需要执行以下操作:

  • 登录用户
  • 获取用户的设置
  • 将用户的设置解析为 JSON
  • 加载当天的视频( 仅对已经注册用户可用)
  • 根据用户的设置更改页面颜色的background
  • 播放视频,如果用户已经启用自动播放

这里说明了任务及其相互依赖性:

An example network of asynchronous tasks.

以下是如何在这里任务图表中使用 promiseDAG():

function login() {
 return.. . // a promise that resolves to the username on successful login}function fetchSettings(username) {
 return fetch('./settings/' + username, {method: 'get'});
}// the argument received here is a Response from fetchfunction parseSettings(settings) {
 return settings.json();
}// ignore the username argument, since we don't need itfunction loadVideo() {
 returnnew Promise((resolve, reject) => {
 var video = document.createElement("video");
 video.addEventListener("canplay", resolve(video)); // resolve when ready to play video.src = "video.mp4";
 });
}// the argument received here is the settings as JSONasync function setBackground(settings) {
 document.body.style.background = settings.favoritecolor;
}
async function play(video, settings) {
 if(settings.autoplay) {
 video.play();
 }
}
promiseDAG([login, // 0 fetchSettings, // 1 parseSettings, // 2 loadVideo, // 3 setBackground, // 4 play, // 5 ],
 [[],
 [0],
 [1],
 [0],
 [2],
 [3,2], // match order of arguments ]);

更新&演示

我在学习JavaScript承诺的时候,曾经写过这段代码。 除了可以下载这篇文章,它是托管在GitHub上。

这里有一个使用 promiseDAG()的演示页面。 它以图形方式展示了( 在这种情况下是简单的超时)的有向无环图,以及它们的状态。 本文还包含了演示代码。


JAVA  Javascript  FLEX  计划  
相关文章