理解JS承诺

时间:2014-12-24 13:59:00

标签: 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);
}
);

这里有link来执行此代码。

正如您所预测的那样,第一个p1被解析,然后是p2,最后一个结果然后打印出resolv值。

但是API引用了以下内容:

"then" returns a new promise equivalent to the value you return from 
onFulfilled/onRejected after being passed through Promise.resolve

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

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

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

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

7 个答案:

答案 0 :(得分:10)

关于承诺解决方案

您在这里见证的事情称为递归then能够解决。 Promises / A +规范中的promise解析过程包含以下子句:

  

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

ES6承诺规范(承诺解包)包含类似的条款。

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

在实践中

这意味着如果onFulfilledthen)返回一个值,请尝试自己“解析”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
});

例如:

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

那:

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

这是个好主意吗?

这是承诺规范过程中备受争议的主题,第二个链式方法没有表现出这种行为,但是已经讨论过了(仍然可以在Chrome中使用,但很快就会删除)。你可以阅读整个辩论in this esdiscuss thread此行为是出于实际原因,因此您无需手动执行此操作。

其他语言

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

答案 1 :(得分: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);
});

一般来说,当从“then”子句中返回一个承诺时,封装承诺的“then”函数只有在内部“then”结束后才会被标记为已完成。

所以,如果“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!");
};

所以输出将是:

B! 
Done!

请注意,如果你没有“返回”内部承诺,情况就不会这样了:

// "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!");
};

这里我们不知道先输出什么。它可能是“B!”或“完成!”。

答案 2 :(得分:1)

请检查以下有关 promises 工作方式的示例:

The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.

console.log('person1: shoe ticket');
console.log('person2: shoe ticket');

const promiseGirlFriendBringingTickets = new Promise((resolve, reject) => {
	setTimeout(() => {
		resolve('ticket');
	}, 3000);
});

promiseGirlFriendBringingTickets.then((t) => {
	console.log(`person3: show ${t}`);
})

console.log('person4: shoe ticket');
console.log('person5: shoe ticket');

答案 3 :(得分:0)

我不知道在实际的promises库中是如何完成的,但我能够通过以下方式重新创建此功能: 1)每个承诺都有waitingPromises属性; 2)then方法返回一个新的promise,原始promise的waitingPromises属性指向新的promise。

通过这种方式,.then()的链创建了一个类似于链表或结构树的结构(每个promise可以有几个等待的promise)。只有在其父母的承诺之后才能解决承诺。承诺已经解决。 .then方法本身立即执行,但它创建的相应承诺仅在以后解决。 我不确定这是一个很好的解释,并希望了解其他可能的方法。

答案 4 :(得分:0)

承诺then返回promise object,而不是promise's resolved value。我分叉了你的JsFiddle,并添加了我的一些try this

promise.thenpromise object解决后立即执行。

答案 5 :(得分:0)

通常代码是同步的-一个语句执行 like(fileopen),并且可以保证下一条语句将紧随 like filewrite()

之后执行。

但是在像nodejs 这样的异步操作中,您应该假设

  • 您不知道何时完成操作。
  • 您甚至不能仅仅因为您先发出一个请求,然后再发出另一个请求,就认为它们会以该顺序返回
  • 回调是处理JavaScript中异步代码的标准方式
  • 但是promise是处理异步代码的最佳方法。
  • 这是因为回调使错误处理变得困难,并导致难看的嵌套代码。
  • 哪位用户和程序员不容易阅读,所以承诺就是这样

答案 6 :(得分:0)

您可以将Promise视为某些后台任务的包装。它具有需要在后台执行的功能。

使用promise的最合适的地方是一些代码依赖于某些后台处理,并且它需要知道已执行的后台任务的状态。为此,后台任务本身接受两个回调resolvereject,以便将其状态传达给依赖于它的代码。用外行人的话来说,此代码是promise链中的代码。

当后台任务使用某些参数调用resolve回调时。它标志着后台操作成功,并将后台操作的结果传递到下一个要执行的下一个then块。如果调用reject并将其标记为不成功,则将执行第一个catch块。 在您的自定义promise中,您可以将错误obj传递到拒绝回调,以便下一个catch块知道后台任务中发生的错误。