理解 javascript Promise;堆栈和链接

IT技术 javascript promise chain
2021-01-31 03:15:43

我遇到了一些关于 javascript Promise的问题,尤其是堆叠链。

谁能向我解释这些不同实现之间的区别(如果有的话!)?

实施 1

var serverSidePromiseChain;
serverSidePromiseChain = async().then(function(response) {
    console.log('1', response);
    return response;
}).then(function(response) {
    console.log('2', response);
    return true;
}).then(function(response) {
    console.log('3', response); // response expected to be 'true'
    return async3();
}).then(function(response) {
    console.log('4', response);
    return async4();
})

实施2

var serverSidePromiseChain;
serverSidePromiseChain = async().then(function(response) {
    console.log('1', response);
    return response;
});

serverSidePromiseChain.then(function(response) {
    console.log('2', response);
    return true;
})
serverSidePromiseChain.then(function(response) {
    console.log('3', response); // response expected to be 'true'
    return async3();
})
serverSidePromiseChain.then(function(response) {
    console.log('4', response);
    return async4();
})

实施3

var serverSidePromiseChain;
serverSidePromiseChain = async().then(function(response) {
    console.log('1', response);
    return response;
});

serverSidePromiseChain = serverSidePromiseChain.then(function(response) {
    console.log('2', response);
    return true;
})
serverSidePromiseChain = serverSidePromiseChain.then(function(response) {
    console.log('3', response); // response expected to be 'true'
    return async3();
})
serverSidePromiseChain = serverSidePromiseChain.then(function(response) {
    console.log('4', response);
    return async4();
})

链的一部分返回值(步骤 2 中的“真”)这一事实是否会改变行为?Promise是否要求所有返回值都是异步Promise以保持行为?

3个回答

您正在说明链接和分支之间的区别。链接将会对多个异步操作进行排序,以便在前一个操作完成时开始一个操作,您可以链接任意数量的项目以一个接一个地进行排序。

当一个触发器操作完成时,分支将多个异步操作挂钩到同时进行。

实现 1 和 3 是相同的。他们被拴住了。实现 3 只是使用一个临时变量来链接,而实现 1 只是.then()直接使用返回值执行没有区别。这些.then()处理程序将以串行方式调用。

实现 2 是不同的。它是分支的,而不是链式的。因为所有后续.then()处理程序都附加到完全相同的serverSidePromiseChainPromise,它们都只等待第一个Promise得到解决,然后所有后续异步操作都在同一时间进行(不像其他两个选项那样连续)。


深入了解如何使用 Promise 可能有助于理解这一点。

当你这样做时(场景 1 和 3):

p.then(...).then(...)

发生的情况如下:

  1. 解释器接受你的p变量,找到.then()方法并调用它。
  2. .then()方法只存储它传递的回调,然后返回一个新的Promise对象。它此时不调用它的回调。这个新的 Promise 对象与原始的 Promise 对象和它存储的回调相关联。在双方都满意之前,它不会解决。
  3. 然后.then()调用新返回的Promise的第二个处理程序。同样,该.then()Promise处理程序仅存储.then()回调,它们尚未执行。
  4. 然后在未来的某个时候,原始的 Promisep会被它自己的异步操作解决。当它得到解决时,它会调用resolve它存储的任何处理程序。其中一个处理程序将是.then()对上述链中第一个处理程序的回调如果该回调运行到完成并返回任何内容或静态值(例如,不返回Promise本身),那么它将解析它创建的Promise,以在.then()第一次调用后返回当该Promise被解析时,它将调用.then()上面第二个处理程序安装的解析处理程序,依此类推。

执行此操作时(场景 2):

p.then();
p.then();

p这里的一个 promise存储了来自两个.then()调用的解析处理程序当最初的Promisep得到解决时,它将调用两个.then()处理程序。如果.then()处理程序本身包含异步代码并返回Promise,则这两个异步操作将同时进行(类似并行的行为),而不是像场景 1 和 3 那样按顺序进行。

你说“当同一个操作完成时,分支将多个操作挂钩到所有并行运行。” 这是一种误导,因为它可以被理解为说处理程序并行运行,这不是真的(处理程序在同一线程中以串行方式运行,顺序与then调用相应s 的顺序相同)。类似地,最后一句“这两个 .then() 处理程序将并行运行”更明显是错误的:不,.then() 处理程序不会并行运行,但它们调度的异步工作可能会。
2021-03-27 03:15:43
谢谢。我确实认为这是一个重要的区别,并不一定很明显,尤其是对于那些使用您的答案作为材料来了解和巩固我对 javascript 和 promises 工作原理的理解的人(例如我自己)。谢谢你逗我。现在的措辞对我来说非常好。
2021-03-29 03:15:43
@DonHatch - 我对措辞进行了一些调整,但你在这里有点狡辩。这里的概念是分支允许所有异步操作同时进行(通常称为“并行”),而链接强制严格串行操作,因此在第一个异步操作解决之前,第二个异步操作甚至不会启动. 是的,JS 是单线程的,因此在任何情况下实际上都没有 JS 代码并行运行,但是异步操作本身同时进行,这是异步操作的类似并行行为而不是串行行为。
2021-03-31 03:15:43
“实现 2 是不同的。它是分支的,而不是链接的。因为所有后续的 .then() 处理程序都附加到完全相同的 serverSidePromiseChain Promise,它们都只等待第一个Promise得到解决”是(对我来说)最简洁的并明确答复。说得好。谢谢!
2021-04-04 03:15:43

实现#1 和#3 是等效的。实现 #2 不同,因为那里没有链,所有回调都将在同一个Promise上执行。

现在让我们讨论一下 Promise 链。规格告诉:

2.2.7then必须返回一个Promise
2.2.7.1 如果有onFulfilledonRejected返回一个值x,则运行Promise解析过程[[Resolve]](promise2, x)
2.3.3 如果x是一个Promise,则采用其状态

基本上调用then一个 promise 会返回另一个promise,它会根据callback return value. 在您的情况下,您将返回标量值,然后将其沿链传播到下一个Promise。

在您的特定情况下,会发生以下情况:

  • #1:你有7个promise(async调用加上4个then,加上两个来自async3()/ async4),serverSidePromiseChain将指向最后一个promise返回的promise then现在如果返回的Promiseasync()从未被解决/拒绝,那么serverSidePromiseChain也将处于相同的情况。async3()/async4()如果该Promise也未解决/拒绝相同
  • #2:then在同一个promise上被多次调用,额外的promise被创建但是它们不会影响应用程序的流程。一旦返回的promiseasync()将被解析,所有回调将被执行,回调返回的将被丢弃
  • #3:这相当于#1,只是现在你明确地传递了创建的Promise。当返回的promiseasync()得到解决时,将执行第一个回调,它用 解决下一个promise true,第二个回调将相同,第三个将有机会将链转换为失败的链,如果async3()的promise得到被拒绝,与返回async4()Promise的回调相同

Promise 链最适合实际的异步操作,其中操作依赖于前一个的结果,并且您不想编写大量的胶水代码,也不想达到回调地狱

我在我的博客上写了一系列关于 promises 的文章,其中一篇描述了 promises 的链接特性可以在这里找到这篇文章是针对ObjectiveC的,但是原理是一样的。

实现 1 和 3 似乎是等效的。

在实现 2 中,最后 3 个.then()函数都作用于同一个 promise。.then()方法返回一个新的Promise。已履行的Promise的value不能改变。参见Promises/A+ 2.1.2.2您在实施 2 中的评论,即响应预计为真,表明您期望否则。不,response不会是真的(除非这是原始Promise的value)。

让我们试试吧。运行以下代码片段以查看差异:

function async(){ return Promise.resolve("async"); }
function async3(){ return Promise.resolve("async3"); }
function async4(){ return Promise.resolve("async4"); }


function implementation1() {
  logContainer = document.body.appendChild(document.createElement("div"));
  console.log("Implementation 1");
  var serverSidePromiseChain;
  serverSidePromiseChain = async().then(function(response) {
    console.log('1', response);
    return response;
  }).then(function(response) {
    console.log('2', response);
    return true;
  }).then(function(response) {
    console.log('3', response); // response expected to be 'true'
    return async3();
  }).then(function(response) {
    console.log('4', response);
    return async4();
  });
}

function implementation2() {
  logContainer = document.body.appendChild(document.createElement("div"));
  console.log("Implementation 2");
  var serverSidePromiseChain;
  serverSidePromiseChain = async().then(function(response) {
    console.log('1', response);
    return response;
  });
  serverSidePromiseChain.then(function(response) {
    console.log('2', response);
    return true;
  });
  serverSidePromiseChain.then(function(response) {
    console.log('3', response); // response expected to be 'true'
    return async3();
  });
  serverSidePromiseChain.then(function(response) {
    console.log('4', response);
    return async4();
  });
}

function implementation3() {
  logContainer = document.body.appendChild(document.createElement("div"));
  console.log("Implementation 3");
  var serverSidePromiseChain;
  serverSidePromiseChain = async().then(function(response) {
    console.log('1', response);
    return response;
  });
  serverSidePromiseChain = serverSidePromiseChain.then(function(response) {
    console.log('2', response);
    return true;
  });
  serverSidePromiseChain = serverSidePromiseChain.then(function(response) {
    console.log('3', response); // response expected to be 'true'
    return async3();
  });
  serverSidePromiseChain = serverSidePromiseChain.then(function(response) {
    console.log('4', response);
    return async4();
  });
}

var logContainer;
var console = {
  log: function() {
    logContainer.appendChild(document.createElement("div")).textContent = [].join.call(arguments, ", ");
  }
};

onload = function(){
  implementation1();
  setTimeout(implementation2, 10);
  setTimeout(implementation3, 20);
}
body > div {
  float: left;
  font-family: sans-serif;
  border: 1px solid #ddd;
  margin: 4px;
  padding: 4px;
  border-radius: 2px;
}