简体   繁体   English

失序的承诺

[英]Out of Order Promise

Given the following code : 给出以下代码

p = () ->
  foo 5
  new Promise( (resolve, reject) ->
    console.log 'in promise'
    resolve 'done'
  )

foo = (n) ->
  console.log n
  if(n <= 0) then 0 else foo (n-1)

And then I call it: 然后我称之为:

p().then (-> console.log 'hi'), (-> console.log 'there')

The output shows: 输出显示:

5
4
3
2
1
0
in promise
hi

First, if I want the entire body of p to occur in the same Promise , must I wrap it in new Promise ( (resolve, reject) ) -> ... ) ? 首先,如果我希望p的整个主体出现在同一个Promise ,是否必须将其包装在new Promise ( (resolve, reject) ) -> ... )

Second, is it ever possible, by modifying foo , that hi would print before 5/4/3/2/1/0 or in promise ? 其次,是否有可能通过修改foo使hi5/4/3/2/1/0之前或in promise打印?

Lastly, in which circumstances would the success case of then execute before the p() body fully completes? 最后,在哪种情况下p()主体完全完成之前, then执行成功的案例会如何?

First, if I want the entire body of p to occur in the same Promise, must I wrap it in new Promise ( (resolve, reject) ) -> ... )? 首先,如果我希望p的整个主体出现在同一个Promise中,是否必须将它包装在新的Promise中((resolve,reject))-> ...)?

Well, yes. 嗯,是。 If you want the entire body of p to happen in the same promise then you have to put the entire body of p in the same promise. 如果您希望p的整个主体在相同的承诺中发生,则必须将p的整个主体置于相同的承诺中。 However, as Bergi mentioned in the comments, this still doesn't affect your overall code behavior. 但是,正如Bergi在评论中提到的那样,这仍然不会影响您的整体代码行为。 Promise construction is a synchronous process, meaning as soon as you create a Promise, the code in its body runs. Promise的构建是一个同步过程,这意味着一旦您创建Promise,主体中的代码就会运行。 The part that happens later is the promise resolution (success or fail). 稍后发生的部分是承诺解决(成功或失败)。

p = () ->
  foo 5                             // happens synchronously
  new Promise( (resolve, reject) ->
    console.log 'in promise'        // happens synchronously
    resolve 'done'                  // happens asynchronously
  )

The following code is equivalent: 以下代码是等效的:

p = () -> 
  new Promise( (resolve, reject) ->
    foo 5                           // happens synchronously
    console.log 'in promise'        // happens synchronously
    resolve 'done'                  // happens asynchronously
  )

If you give a more specific use-case, I might be able to give a more specific answer. 如果您给出更具体的用例,我也许可以给出更具体的答案。

Second, is it ever possible, by modifying foo, that hi would print before 5/4/3/2/1/0 or in promise? 其次,是否有可能通过修改foo在5/4/3/2/1/0之前或应许中打印hi?

Promises resolution, as per specification, always happens asynchronously (in form of microtasks in the Event Loop). 承诺的分辨率,按照规范, 始终异步操作(在的形式microtasks事件循环)。 This means that all synchronous code, such as your loop in foo , must finish before any promise resolutions happen. 这意味着所有同步代码(例如foo的循环)必须在任何promise解析发生之前完成。

To quote Jake Archibald 引用杰克·阿奇博尔德(Jake Archibald)

Microtasks are usually scheduled for things that should happen straight after the currently executing script, such as reacting to a batch of actions, or to make something async without taking the penalty of a whole new task. 通常,微任务是为应在当前正在执行的脚本之后立即发生的事情而安排的,例如对一批动作做出反应,或使某些事情异步而不承担整个新任务的代价。 The microtask queue is processed after callbacks as long as no other JavaScript is mid-execution, and at the end of each task. 只要没有其他JavaScript在执行中间,微任务队列就会在回调之后进行处理,并且在每个任务结束时进行处理。 Any additional microtasks queued during microtasks are added to the end of the queue and also processed. 在微任务期间排队的所有其他微任务都将添加到队列的末尾并进行处理。 Microtasks include mutation observer callbacks, and as in the above example, promise callbacks. 微任务包括变异观察者回调,并如上例所示,承诺回调。

Therefore, the only way for you to make things in foo happen after things in p , is to force foo to run asynchronously and after p is finished. 因此,使foo发生在p事情之后的唯一方法是强制foo p完成后异步运行。

One way to tackle this is to use setTimeout to delay foo 's code after the resolution of the Promise in p . 解决此问题的一种方法是使用setTimeoutp中的Promise解析之后延迟foo的代码。 The reason this works is because setTimeout schedules a regular task in the next iteration of the Event Loop while microtasks (and therefore Promises) are specified to run in the same iteration of the Event Loop. 之所以起作用,是因为setTimeout在事件循环的下一次迭代中安排了常规任务,而微任务(因此也是承诺)被指定为在事件循环的同一迭代中运行。

Here's a JS demonstration: 这是一个JS演示:

Note : this might not work in every browser. 注意 :这可能不适用于所有浏览器。 I tested it in Chrome Version 54.0.2840.71. 我在Chrome版本54.0.2840.71中对其进行了测试。 Jake's article has more details about specific browser bugs. Jake的文章提供了有关特定浏览器错误的更多详细信息。

 const foo = (n) => { setTimeout(() => { console.log(n); if (n <= 0) { return 0; } else { return foo(n - 1); } }); } const p = () => { foo(5); return new Promise((resolve) => { console.log('in promise'); resolve('done'); }); } p().then(() => console.log('hi')); /* Output: in promise hi 5 4 3 2 1 0 */ 

Lastly, in which circumstances would the success case of then execute before the p() body fully completes? 最后,在哪种情况下p()主体完全完成之前,然后执行成功的案例会如何?

In no circumstance because the success case of p().then depends on that internal promise. 在任何情况下都不会因为p().then the成功案例而取决于该内部承诺。 The success callback passed into then cannot be called until the Promise on which we called then is resolved (either succeeded or failed). 传递到成功回调then不能叫,直到我们称之为无极then解决(无论成功或失败)。

Here's another useful article about tackling these timing situations when writing a JS framework. 这是另一篇有关编写JS框架时解决这些时序问题的有用文章

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM