是JavaScriptPromise异步的
Are JavaScript Promise asynchronous?
只是一个简单的澄清问题:JavaScript Promise
是异步的吗?我读了很多关于Promise
和异步编程(即ajax请求)的文章。如果Promise
不是异步的,我们如何使它成为异步的呢?
例如,我有一个函数将函数f
和参数数组args
包装在Promise
中。f
本质上没有任何东西是异步的。
function getPromise(f, args) {
return new Promise(function(resolve, reject) {
var result = f.apply(undefined, args);
resolve(result);
});
}
为了实现这种异步,我阅读了一些SO的文章,并决定setTimeout
是许多人建议的使代码不阻塞的方法。
function getPromise(f, args) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
var r = f.apply(undefined, args);
resolve(r);
}, 0);
});
}
setTimeout
的这种方法能使代码在Promise
中不阻塞吗?
(请注意,我不依赖任何第三方Promise API,只依赖浏览器支持的内容)。
我认为您的工作存在误解。JavaScript代码是总是*阻塞;这是因为它在单个线程上运行。Javascript中异步编码风格的优点在于,像I/O这样的外部操作不需要阻塞该线程。但是,处理I/O响应的回调仍处于阻塞状态,没有其他JavaScript可以同时运行。
*除非您考虑运行多个进程(或在浏览器上下文中运行WebWorkers)。
现在针对您的具体问题:
只是一个快速澄清的问题:JavaScriptPromise是异步的吗?
不,传递到Promise构造函数的回调是立即同步执行的,尽管肯定可以启动异步任务,例如超时或写入文件,然后等待异步任务完成后再解析Promise;事实上,这是promise的主要用例。
这种带有setTimeout的方法能使代码在Promise中不阻塞吗?
不,它所做的只是改变执行顺序。脚本的其余部分将一直执行到完成,然后当它没有其他事情可做时,将执行setTimeout的回调。
澄清:
console.log( 'a' );
new Promise( function ( ) {
console.log( 'b' );
setTimeout( function ( ) {
console.log( 'D' );
}, 0 );
} );
// Other synchronous stuff, that possibly takes a very long time to process
console.log( 'c' );
上述程序决定性地打印:
a
b
c
D
这是因为setTimeout的回调直到主线程没有任何事情可做(在记录"c"之后)才会执行。
我知道这是一个7年前的问题,但:
答案:
- Promise块是同步的
- Promise回调块(resolve、reject)是同步的
- 如果Promise块使用setTimeout块,则回调
call
(而不是块本身)可以是异步的
因此,为了回答Jane Wayne正在做的直接问题,是的,使用setTimeout块将使Promise块不阻塞。
解释:
Javascript是同步的,它只有一个线程。Javascript引擎使用堆栈(Call Stack
)来执行所有内容(LIFO
)。
Web API是浏览器的一部分。例如,在那里可以找到setTimeout
作为Window
对象的一部分。
两个世界之间的通信是通过使用Event Loop
管理的两个队列(Microtask和Macrotask队列,即FIFO
)来完成的,这是一个不断运行的进程。
事件循环、微任务和宏任务队列
执行情况如下:
- 使用调用堆栈执行所有脚本
Event Loop
一直检查Call Stack
是否为空- 如果
Event Loop
发现Call Stack
为空,则开始从Microtask queue
向Call Stack
传递回调(如果有的话) - 一旦
Microtask queue
为空,则Event Loop
对Macrotask queue
执行相同操作
因此,进入Promise
世界。
- Promise块同步执行
- 在
Promise
块中,可以有一些在Javascript引擎之外执行的东西(例如setTimeout
块),这就是我们所说的异步,因为它在Javascript线程之外运行 - Promise的回调(
resolve
和reject
)的执行可以认为是异步的,但块本身是同步执行的
因此,如果在Promise块中没有任何异步块,Promise将阻止您的执行。而且,如果在回调部分没有任何异步块,Promise将阻止您的执行。
示例:
console.log(`Start of file`)
const fourSeconds = 4000
// This will not block the execution
// This will go to the Macrotask queue
// Microtask queue has preference over it, so all the Promises not using setTimeout
setTimeout(() => {
console.log(`Callback 1`)
})
// This will block the execution because the Promise block is synchronous
// But the result will be executed after all the Call Stack
// This will be the first Microtask printed
new Promise((resolve, reject) => {
let start = Date.now()
while (Date.now() - start <= fourSeconds) { }
resolve(`Promise block resolved`)
}).then(result => console.log(result))
// This will not block the execution
// This will go into the Macrotask queue because of setTimeout
// Will block all the Macrotasks queued after it
// In this case will block the Macrotask "Callback 2" printing
new Promise((resolve, reject) => {
setTimeout(() => resolve(`Promise callback block resolved`))
}).then(result => {
let start = Date.now()
while (Date.now() - start <= fourSeconds) { }
console.log(result)
})
// This will not block the execution
// This will go to the Macrotask queue
// Microtask queue has preference over it
// Also the previous Macrotasks has preference over it, so this will be the last thing to be executed
setTimeout(() => {
console.log(`Callback 2`)
})
// This will not block the execution
// It will go to the Microtask queue having preference over the setTimeout
// Also the previous Microtasks has preference over it
Promise.resolve(`Simply resolved`).then(result => console.log(result))
console.log(`End of file`)
/*
Output:
Start of file
[... execution blocked 4 seconds ...]
End of file
Promise block resolved
Simply resolved
Callback 1
[... execution blocked 4 seconds ...]
Promise callback block resolved
Callback 2
*/
const p = new Promise((resolve, reject) => {
if (1 + 1 === 2) {
resolve("A");
} else {
reject("B");
}
});
p.then((name) => console.log(name)).catch((name) => console.log(name));
console.log("hello world");
Promise在处于挂起状态时不会阻塞下一行。因此,它是异步工作的。
您的MDN参考非常有用。Thx。如果你运行这个,你应该看到异步输出。
================================================================异步使用";承诺"
const log = console.log;
//---------------------------------------
class PromiseLab {
play_promise_chain(start) {
//"then" returns a promise, so we can chain
const promise = new Promise((resolve, reject) => {
resolve(start);
});
promise.then((start) => {
log(`Value: "${start}" -- adding one`);
return start + 1;
}).then((start) => {
log(`Value: "${start}" -- adding two`);
return start + 2;
}).then((start) => {
log(`Value: "${start}" -- adding three`);
return start + 3;
}).catch((error) => {
if (error) log(error);
});
}
}
//---------------------------------------
const lab = new PromiseLab();
lab.play_promise_chain(100);
lab.play_promise_chain(200);
输出应该是异步的,比如:
Value: "100" -- adding one
Value: "200" -- adding one
Value: "101" -- adding two
Value: "201" -- adding two
Value: "103" -- adding three
Value: "203" -- adding three
================================================================同步使用";MyPromise";(例如基本js代码)
const log = console.log;
//---------------------------------------
class MyPromise {
value(value) { this.value = value; }
error(err) { this.error = err; }
constructor(twoArgFct) {
twoArgFct(
aValue => this.value(aValue),
anError => this.error(anError));
}
then(resultHandler) {
const result = resultHandler(this.value);
return new MyPromise((resolve, reject) => {
resolve(result);
});
}
catch(errorHandler) {
errorHandler(this.error());
}
}
//--------------------------------------
class MyPromiseLab {
play_promise_chain(start) {
//"then" returns a promise, so we can chain
const promise = new MyPromise((resolve, reject) => {
resolve(start);
});
promise.then((start) => {
log(`Value: "${start}" -- adding one`);
return start + 1;
}).then((start) => {
log(`Value: "${start}" -- adding two`);
return start + 2;
}).then((start) => {
log(`Value: "${start}" -- adding three`);
return start + 3;
}).catch((error) => {
if (error) log(error);
});
}
}
//---------------------------------------
const lab = new MyPromiseLab();
lab.play_promise_chain(100);
lab.play_promise_chain(200);
输出应同步:
Value: "100" -- adding one
Value: "101" -- adding two
Value: "103" -- adding three
Value: "200" -- adding one
Value: "201" -- adding two
Value: "203" -- adding three
- esri javascript异步打印
- JavaScript异步问题
- $translateProvider.useStaticFilesLoader的Angular Translate异步定
- 异步facebook功能
- 异步并行错误
- 在Redux中,我应该在哪里编写复杂的异步流
- 角度异步http自动完成
- 如何从SeleniumWebdriver获取异步Javascript响应
- 如何使用异步调用更改工厂的变量
- 在等待异步任务时永久循环
- 如何在异步函数中使用javascript对象
- 调用后不异步Ajax忽略函数
- learnyounode#9杂耍异步
- 异步获取数据使用JavaScript同步获取数据
- Meteor:异步回调问题
- 如何以异步方式打印q中的项目
- javascript函数中的异步与同步.(Node.js)
- Fresh Spark Install+Homestead上的Vue异步堆栈跟踪错误
- Meteor:异步函数回调异常:onAfterAction
- 函数式javascript读取、异步、写入结果