为什么JavaScript Promise然后处理程序在其他代码之后运行?

时间:2016-06-27 16:51:43

标签: javascript promise ecmascript-6 es6-promise

我只是想提高我对JavaScript Promises如何运作的理解。我创造了以下情况:

LOG 'FOO'
RUN CALLBACK LOGGING 'CALLBACK'
LOG 'BAR'

期望所有功能立即完成 (我的意思是他们不会花费过多/未知的时间来完成您将使用异步操作来完成)以便上面的顺序操作将按此顺序进行。

您可以通过以下方式编写此内容:

function foo(cb) {
  // LOG 'FOO'
  console.log('foo');
  // RUN CALLBACK
  cb();
}

function callback() {
  // LOG 'CALLBACK'
  console.log('callback');
}

foo(callback);

console.log('bar');

根据我在开头指定的情况产生预期的输出。

> foo
> callback
> bar

可以以下列方式编写它:

function foo() {
  return new Promise((resolve) => {
    // LOG 'FOO'
    console.log('foo');
    return resolve(null);
  });
}

function callback() {
  // LOG 'CALLBACK'
  console.log('callback');
}

foo().then(callback);

// LOG 'BAR'
console.log('bar');

这种情况会产生以下结果:

> foo
> bar
> callback

这是我不清楚的地方,因为我希望foo立即完成 ,以便callback运行并在'callback'之前记录bar } logs 'bar'

3 个答案:

答案 0 :(得分:3)

相关规格如下:

  1. Promises/A+ point 2.2.4

      在执行上下文堆栈仅包含平台代码之前,不得调用

    onFulfilledonRejected。 [3.1]。

    并注意3.1(强调我的):

      

    此处“平台代码”表示引擎,环境和承诺实现代码。 实际上,此要求可确保onFulfilledonRejected异步执行,然后调用事件循环,并使用新堆栈。这可以使用“{-1}}或setTimeout等”宏任务“机制,或setImmediateMutationObserver等”微任务“机制来实现。由于promise实现被认为是平台代码,它本身可能包含一个任务调度队列或“trampoline”,其中调用处理程序。

  2. ECMAScript 6.0(基于Promises / A +)有点难以清晰地摘录,但process.nextTick已解决as in section 25.4.5.3.1

      
        
    1. 否则承诺的[[PromiseState]]内部广告位的值为then

           

      一个。让 value promise 的[[PromiseResult]]内部插槽的值。

           

      湾执行EnqueueJob("fulfilled",PromiseReactionJob,«fulfillReaction,value»)。

    2.   
    3. 否则,如果promise的[[PromiseState]]内部广告位的值为"PromiseJobs"

           

      一个。让 reason promise 的[[PromiseResult]]内部插槽的值。

           

      湾执行EnqueueJob("rejected",PromiseReactionJob,«rejectReaction,原因»)。

    4.   

    重要的EnqueueJob操作在section 8.4 ("Jobs and Job Queues")中定义,以前言(粗体是我的)为特色:

      

    只有在没有正在运行的执行上下文且执行上下文堆栈为空时才能启动作业的执行。 [...]启动作业执行后,作业始终执行完成。 在当前正在运行的作业完成之前,不会启动任何其他作业。

  3. 在实践中,这可以让您做出一些简单而一致的陈述:

    • 您可以指望"PromiseJobs"then(等)始终异步行为,永远不会同步。
    • 即使在另一个Promise中明确解析了一个Promise,您也永远不会在同一个堆栈上看到多个catchthen处理程序。这也意味着递归Promise执行不会像普通函数调用一样冒着堆栈溢出的风险,但如果你在病态情况下不小心使用递归闭包,你仍然可能会用完堆空间。
    • catchthen处理程序中排队的耗时操作将永远不会阻止当前线程,即使Promise已经解决,因此您可以排队一些异步操作而无需担心订单或承诺状态。
    • catchtry之外永远不会有then封闭,即使在已经解决的Promise上调用catch时也是如此,所以没有歧义平台是否应该处理抛出的异常。

答案 1 :(得分:2)

由于承诺的运作方式,这是不可能的。 即使是立即解决的承诺也会在下一个时间点运行,你想要的是同步功能而不是承诺。

见例如:

PopupDateField

在不删除conf = SparkConf().setAll(( ("spark.python.profile", "true" if args.profile else "false"), ("spark.task.maxFailures", "20"), ("spark.driver.cores", "4"), ("spark.executor.cores", "4"), ("spark.shuffle.service.enabled", "true"), ("spark.dynamicAllocation.enabled", "true"), )) # TODO could this be set somewhere in cosr-ops instead? executor_environment = {} if config["ENV"] == "prod": executor_environment = { "PYTHONPATH": "/cosr/back", "PYSPARK_PYTHON": "/cosr/back/venv/bin/python", "LD_LIBRARY_PATH": "/usr/local/lib" } sc = SparkContext(appName="Common Search Index", conf=conf, environment=executor_environment) # First, generate a list of all WARC files warc_filenames = list_warc_filenames() # Then split their indexing in Spark workers warc_records = sc.parallelize(warc_filenames, 4).flatMap(iter_records) 函数的情况下,无法在test2之前打印测试,因为即使wait参数为0,它也会运行下一个tick,这意味着它将在所有同步代码运行时运行。

答案 2 :(得分:2)

我真的不是直言不讳,但这是因为这是规范说他们工作的方式。如果在承诺中的代码完成后需要在某一点运行一段代码,那么您应该使用promise链。一旦你将异步代码引入混合中,尝试将它与依赖的同步代码混合是一个坏主意。

只要您需要依赖异步代码的东西,

菊花链承诺:

.table-row { display: flex;}
.table-cell {width: 33%; }
.span2 { width: 66%;}

<强>产地:

function foo() {
    console.log('foo');
    return Promise.resolve();
}

function callback() {
    console.log('callback');
}

function consoler() {
    console.log('bar');
}

foo().then(callback).then(consoler);