理解JS承诺

Qui*_*rty 17 javascript promise

我想更深入地了解Promises如何在内部工作.因此我有一些示例代码:

var p1 = new Promise(
  function(resolve, reject) {
    window.setTimeout(
      function() {
        resolve('res called')
      }, 2000);
  });


var p2 = new Promise(
  function(resolve, reject) {
    window.setTimeout(
      function() {
        resolve('res called')
      }, 2000);
  });


function chainPromises() {
  return p1.then(function(val) {
    console.log("p1");
    return p2.then(function(val) {
      console.log("p2");
      return val;
    });
  });
}

chainPromises().then(function(val) {
  console.log(val);
});
Run Code Online (Sandbox Code Playgroud)

这是执行此代码的链接.

正如您所预测的那样,首先解决p1,之后是p2,然后最终打印出resolv值.

但API参考声明如下:

var p1 = new Promise(
  function(resolve, reject) {
    window.setTimeout(
      function() {
        resolve('res called')
      }, 2000);
  });


var p2 = new Promise(
  function(resolve, reject) {
    window.setTimeout(
      function() {
        resolve('res called')
      }, 2000);
  });


function chainPromises() {
  return p1.then(function(val) {
    console.log("p1");
    return p2.then(function(val) {
      console.log("p2");
      return val;
    });
  });
}

chainPromises().then(function(val) {
  console.log(val);
});
Run Code Online (Sandbox Code Playgroud)

因此,知道什么时候执行"then"函数会很有趣?因为代码中的最后一个"then"链接到chainPromises(),我首先想到它会在函数chainPromises()返回一些东西后执行(在这种情况下是另一个promise).

如果是这种情况,那么最终"then"函数的"val"将是返回的promise.但相反,最终的"然后"等待,直到返回的第一个"然后"内的所有承诺都已解决.这绝对有道理,因为通过这种方式,"then"函数可以堆叠,但我不知道如何完成这个,因为API规范.并没有真正涵盖"then"返回的内容以及执行"then"函数的时间.

或者换句话说,为什么最后的"then"函数会等到所有Promise在chainPromises()函数内解析,而不是像API文档所说的那样等待第一个返回的对象.

我希望我能说清楚我的意思.. :)

Ben*_*aum 12

关于Promise解决方案

你在这里见证的东西叫做递归then分辨率.Promises/A +规范中的promise解析过程包含以下子句:

onFulfilled或onRejected返回值x,运行Promise Resolution Procedure [[Resolve]](promise2,x)

ES6承诺规范(承诺展开)包含类似的条款.

这要求当一个resolve操作发生时:在promise构造函数中,通过调用Promise.resolve或在你的情况下在一个then链中,一个promise实现必须递归地解包返回的值,如果它是一个promise.

在实践中

这意味着if onFulfilled(then)返回一个值,尝试自己"解析"promise值,从而递归地等待整个链.

这意味着以下内容:

promiseReturning().then(function(){
    alert(1);
    return foo(); // foo returns a promise
}).then(function(){
    alert(2); // will only run after the ENTIRE chain of `foo` resolved
              // if foo OR ANY PART OF THE CHAIN rejects and it is not handled this 
              // will not run
});
Run Code Online (Sandbox Code Playgroud)

例如:

promiseReturning().then(function(){
    alert(1);
    return Promise.resolve().then(function(){ throw Error(); });
}).then(function(){
    alert("This will never run");
});
Run Code Online (Sandbox Code Playgroud)

然后:

promiseReturning().then(function(){
    alert(1);
    return Promise.resolve().then(function(){ return delay(2000); });
}).then(function(){
    alert("This will only run after 2000 ms");
});
Run Code Online (Sandbox Code Playgroud)

这是个好主意吗?

这是承诺规范过程中备受争议的话题,第二个没有表现出这种行为的链式方法已被讨论但是已经决定(仍然可以在Chrome中使用,但很快就会删除).你可以在这个esdescuss线程中阅读有关整个辩论的内容.此行为是出于实际原因,因此您不必手动执行此操作.

在其他语言中

值得一提的是,其他语言不这样做,Scala中的未来或C#中的任务都没有这个属性.例如,在C#中,您必须调用Task.Unwrap一个任务才能等待其链解析.


dco*_*der 6

让我们从一个简单的角度开始:"chainPromises"返回一个承诺,所以你可以这样看待它:

// Do all internal promises
var cp = chainPromises();

// After everything is finished you execute the final "then".
cp.then(function(val) {
    console.log(val);
});
Run Code Online (Sandbox Code Playgroud)

一般来说,当返回从"那么"条款中的承诺,封装承诺的"那么"功能将被完成内部"然后"完成后,才显着.

所以,如果"a"是承诺,"b"是承诺:

// "a"'s "then" function will only be marked as finished after "b"'s "then" function has finished.  
var c = a.then(function () {
    return b.then(function () {
        console.log("B!");
    };
};

// c is a promise, since "then" always returns a promise.    
c.then(function() {
    console.log("Done!");
};
Run Code Online (Sandbox Code Playgroud)

所以输出将是:

B! 
Done!
Run Code Online (Sandbox Code Playgroud)

请注意,如果您没有"返回"内部承诺,情况就不是这样了:

// "a"'s "then" function will only be marked as finished without waiting for "b"'s "then" to finish.  
var c = a.then(function () {
    // Notice we're just calling b.then, and don't "return" it. 
    b.then(function () {
        console.log("B!");
    };
};

// c is a promise, since "then" always returns a promise.    
c.then(function() {
    console.log("Done!");
};
Run Code Online (Sandbox Code Playgroud)

在这里,我们不知道先输出什么.它可能是"B!" 或"完成!".