为什么JavaScript承诺然后处理程序运行在其他代码之后
Why does JavaScript Promise then handler run after other code?
我只是想提高我对JavaScript承诺如何工作的理解。我创建了以下情况:
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
在bar
日志'bar'
之前运行并记录'callback'
相关规格在这里:
-
Promises/A+ point 2.2.4:
在执行上下文堆栈只包含平台代码之前,不能调用
onFulfilled
或onRejected
。[3.1]。和注3.1(强调我的):
这里的"平台代码"是指引擎、环境和承诺实现代码。在实践中,该要求确保
onFulfilled
和onRejected
在调用事件循环之后异步执行,并且具有新的堆栈。这可以通过"宏任务"机制(如setTimeout
或setImmediate
)或"微任务"机制(如MutationObserver
或process.nextTick
)来实现。由于承诺实现被认为是平台代码,因此它本身可能包含一个任务调度队列或"蹦床",其中调用处理程序。 -
ECMAScript 6.0(基于Promises/A+)有点难以清晰地摘录,但
then
解析如第25.4.5.3.1节所示:如果promise s [[PromiseState]] internal slot的值为
"fulfilled"
,。设value为promise的[[PromiseResult]]内部槽位的值。
b。执行EnqueueJob(
"PromiseJobs"
, PromiseReactionJob, «fulfillReaction, value»)。如果promise的[[PromiseState]]内部槽位的值为
"rejected"
,。设reason为promise的[[PromiseResult]]内部槽位的值。
b。执行EnqueueJob(
"PromiseJobs"
, PromiseReactionJob, «rejectReaction, reason»)
重要的EnqueueJob操作在第8.4节("作业和作业队列")中定义,在前言(粗体是我的)中有如下特点:
只有当没有正在运行的执行上下文且执行上下文堆栈为空时,才可以启动Job的执行。[…Job一旦开始执行,就会一直执行到完成。在当前运行的作业完成之前,不能启动其他作业。
在实践中,这使您可以编写一些简单而一致的语句:
- 你可以指望
then
或catch
(等)总是异步行为,从不同步。你永远不会在同一个堆栈上看到多个then
或catch
处理程序,即使一个Promise在另一个Promise中显式解析。这也意味着递归的Promise执行不会像普通的函数调用那样有堆栈溢出的风险,尽管在病态情况下,如果不小心使用递归闭包,仍然会耗尽堆空间。 - 在
then
或catch
处理程序中排队的耗时操作永远不会阻塞当前线程,即使承诺已经解决,所以你可以将许多异步操作排队,而不必担心顺序或承诺状态。在then
或catch
之外永远不会有封闭的try
块,即使在已经解决的Promise上调用then
,所以平台是否应该处理抛出的异常没有歧义。
由于承诺的工作方式,这是不可能的。即使是立即解析的promise也会在下一个tick运行,你需要的是同步函数而不是promise。
请看下面的例子:
setTimeout(function() {
console.log("test");
}, 0);
console.log("test2");
在不删除setTimeout
函数的情况下,在test2之前打印test是不可能的,因为即使wait参数为0,它将在下一个tick中运行,这意味着它将在所有同步代码运行时运行。
我真的不想这么直白,但这是因为规范说它们是这样工作的。如果你需要一段代码在承诺内的代码完成后的某个时间点运行,那么你应该利用承诺链。一旦您将异步代码引入到混合代码中,尝试将其与依赖的、同步的代码混合在一起是一个坏主意。
菊花链承诺当你需要依赖异步代码时:
function foo() {
console.log('foo');
return Promise.resolve();
}
function callback() {
console.log('callback');
}
function consoler() {
console.log('bar');
}
foo().then(callback).then(consoler);
生产:
foo
callback
bar
- 调试一个简单的jQuery函数;想知道是否与其他代码冲突
- Javascript代码会错误其他代码
- Fancybox图像点击可以独立工作,但不能与我的其他代码一起工作
- 阻止其他代码运行,直到 AJAX 请求返回某些内容
- 如何仅触发表单元素与其他代码的部分更改
- 警报正在工作,但 Dreamweaver 中没有其他代码
- 如何使用 javascript 或其他代码将 html 代码调出到其他页面,而不是重写长代码
- eval()正在减慢其他代码的速度,为什么
- Javascript本地存储扰乱了其他代码
- HTML/JavaScript:如何在运行其他代码的同时运行循环
- 如何在我的网页上显示JavaScript和其他代码,以便可以复制
- 运行完所有其他代码后,将显示模式弹出加载屏幕
- 首先向客户端发送html代码(在其他代码之前)
- 为什么空白警报语句会影响其他代码的执行
- 在.js文档中添加onclick和其他代码
- 为什么jQuery源代码在其他代码之前关闭?
- 向现有函数添加其他代码
- jQuery代码破坏其他代码
- 强迫部分代码等待其他代码
- 当通过其他代码更改数据时,不会在文本框上触发更改事件