new Promise里面通常会传入一个function参数。直觉上,传入的function,应该类似于callback,属于被异步调用的。但是实际上,它是同步被执行的。也就是说它是在Promise的构造函数里面被执行的。文献
https://javascript.info/promise-basics提到这个function有个专有的名字:executor:
The function passed to new Promise is called the executor. When new Promise is created, the executor runs automatically.
下面看看一个http request(https://developers.google.com/web/fundamentals/primers/promises)。new promise里面的XMLHttpRequest是即时发出的。所谓异步,体现在Promise.then的执行,是在onload/resolve之后发生的。
function get(url) {
// Return a new promise.
return new Promise(function(resolve, reject) {
// Do the usual XHR stuff
console.log("In Promise's executor");
var req = new XMLHttpRequest();
req.open('GET', url);
req.onload = function() {
// This is called even on 404 etc
// so check the status
if (req.status == 200) {
// Resolve the promise with the response text
resolve(req.status);
}
else {
// Otherwise reject with the status text
// which will hopefully be a meaningful error
reject(Error(req.statusText));
}
};
// Handle network errors
req.onerror = function() {
reject(Error("Network Error"));
};
// Make the request
req.send();
});
}
console.log("Begin");
get('https://jsfiddle.net/').then(function(response) {
console.log("Success!", response);
}, function(error) {
console.error("Failed!", error);
})
console.log("End");
Begin
In Promise's executor
End
Success! 200
Promise.then里面的函数,其执行时机是主线程结束后的某个时间。
关于promise的执行有两种猜测:
var promise1 = new Promise(function(resolve, reject) {
// 输出1
console.log("Inside new Promise, with 300 timeout");
setTimeout(function() {
// 输出4:延迟300ms后才去post resolve 任务。
resolve('foo');
}, 300);
});
promise1.then(function(value) {
console.log(value);
});
var promise2 = new Promise(function(resolve, reject) {
// 输出2
console.log("Inside new Promise");
// resolve并不会立刻输出bar。说明resolve仅仅向某个后台线程池或者主线程post了一个task。
// 等这个主线程或者后台线程池将这个task执行结束,才会开始执行Promise.then里面的函数
//输出3
resolve('bar');
});
promise2.then(function(value) {
console.log(value);
});
// 无论是否有console.log(promise1);,执行顺序不变。
console.log(promise1);
// expected output: [object Promise]
输出是:
> "Inside new Promise, with 300 timeout"
> "Inside new Promise"
> [object Promise]
> "bar"
> "foo"
还有一个例证是,在整个while循环的执行时期,Promise.then里面的函数都没有被执行。
function testPromise2() {
var promise1 = new Promise(function(resolve, reject) {
// 输出1
console.log("Inside new Promise, with 300 timeout");
setTimeout(function() {
// 输出6: 要等主线程结束后的300ms,才会去post resolve task。所谓主线程结束,就是输出End NOP。
resolve('foo');
}, 300);
});
promise1.then(function(value) {
console.log(value);
});
var promise2 = new Promise(function(resolve, reject) {
// 输出2
console.log("Inside new Promise");
// 输出5: 要等主线程结束后,才会去resolve。所谓主线程结束,就是输出End NOP。
resolve('bar');
});
promise2.then(function(value) {
console.log(value);
});
// 输出3
console.log("Start NOP");
let i =0;
while (i < 1000000) {
i++;
let j =0;
while (j < 10000)
j++;
}
// 输出4
console.log("End NOP");
}
输出:
Inside new Promise, with 300 timeout
Inside new Promise
Start NOP
End NOP
bar
foo
http://es6.ruanyifeng.com/#docs/promise
https://stackoverflow.com/questions/32380344/async-update-dom-from-promises
await会导致阻塞。但是这个阻塞的意思并不是阻塞整个async标记的函数,而是同一个async函数,在调用await之后的该函数的所有代码,会被阻塞。await之前的代码,以及async函数之外的代码,都是同步执行的。由于async function返回的是promise,所以await promise和await 一个async function没有区别。
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 3000)
});
let result = await promise; // wait until the promise resolves (*)
console.log("Begin In promise");
console.log(result);
console.log("End In promise");
}
f();
下面这些信息都是在3秒之后输出的。这意味着await promise确实会导致阻塞。
Begin In promise
done!
End In promise
下面看看await 一个async function。
async function foo() {
// 输出 2
console.log("hi");
return 1;
}
async function bar() {
// 输出 1,这里类似executor。
console.log("before await foo, in bar");
const result = await foo();
// 输出 4,await之后的代码,不再属于executor。会被block执行。
console.log("after await foo, in bar");
console.log(result);
console.log("after promise, in bar");
}
bar();
// 输出 3
console.log("lo");
对应的输出如下:
before await foo, in bar
hi
lo
after await foo, in bar
1
after promise, in bar
可以看到await确实是阻塞了。
引用例子:https://stackoverflow.com/questions/42773714/is-async-await-truly-non-blocking-in-the-browser
作者提到 “the code after await is never executed immediately”。这个说法其实不确切,要除掉await function里面的executor部分(如果async function里面有await,那么executor就是await之前的代码),await foo()会导致里面的console.log(“hi”)(executor)立即被执行。但是foo返回的promise,是在整个主线程执行结束之后开始执行的(console.log(result)输出1)。
async function foo() {
//输出1,executor
console.log("hi");
return 1;
}
async function bar() {
const result = await foo();
// 输出3
console.log(result);
}
bar();
// 输出2
console.log("lo");
这段代码的输出是:
hi
lo
1
这说明,即使应用了await,foo里面的console.log(“hi”);仍然是同步的形式被首先执行。但是其返回值是promise,且await foo,所以await之后的代码console.log(result),有点类似被当作一个回调函数,被系统调度到了主任务(没有称主线程,按照文章开头的猜测,promise也可能位于主线程)结束之后开始执行。
在下面的函数bar里面,会等await foo()里面的代码完全结束后,console.log(result)才会有输出。即使console.log(result)修改为任意的console.log(“任意字符”),也是在await foo();完全结束后才会有任意字符的输出。同样的,console.log(“hi”)和waitNOP也是类似executor,会被立即执行。
function waitNOP() {
let i =0;
// 输出2
console.log("waitNOP start");
while (i < 1000000) {
i++;
let j =0;
while (j < 10000)
j++;
}
// 输出3
console.log("waitNOP end");
}
async function foo() {
// 输出1
console.log("hi");
waitNOP();
return 1;
}
async function bar() {
const result = await foo();
// 输出5
console.log(result);
}
bar();
//waitNOP();
// 输出4
console.log("lo");
输出结果:
hi
waitNOP start
waitNOP end
lo
1
下面的例子的console.log(“resolveAfter2Seconds end”); 会很早就执行。但是promise是2秒之后被执行的。这说明:await并不会是说,await的这个表达式里面的所有的内容都会等待。里面部分内容是立刻执行的。但是,await的返回时间,是里面的promise的状态变成resolved的时候。
等待是await promise导致的。
function resolveAfter2Seconds() {
var promise = new Promise(resolve => {
// setTimeout位于executor里面,会被立刻执行
setTimeout(() => {
// 输出3
resolve('resolved');
}, 2000);
});
// 输出2
console.log("resolveAfter2Seconds end");
return promise;
}
async function asyncCall() {
// 输出1
console.log('calling');
// await不会导致整个函数等待。等待的是promise的部分。
var result = await resolveAfter2Seconds();
// 下面的代码会被阻塞两秒。即使注释掉console.log(result);, console.log("asyncCall end") 也要2秒后执行
console.log(result);
console.log("asyncCall end");
// expected output: 'resolved'
}
asyncCall();
输出结果:
> "calling"
> "resolveAfter2Seconds end"
> // 2秒后输出
> "resolved"
> "asyncCall end"
在上面的代码末尾加上一句console.log(“loo”);,会发生什么事情?
输出会是:
calling
resolveAfter2Seconds end
loo
//下面的内容2秒后出现
resolved
asyncCall end
这说明await,只会阻塞他所在的async函数。调用它的sync函数(如果是个async函数调用它呢?),不会被阻塞。源码如下:
function resolveAfter2Seconds() {
var promise = new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
console.log("resolveAfter2Seconds end");
return promise;
}
async function asyncCall() {
console.log('calling');
// await不会导致整个函数等待。等待的是promise的部分。
var result = await resolveAfter2Seconds();
// 下面的代码会被阻塞两秒。即使注释掉console.log(result);, console.log("asyncCall end") 也要2秒后执行
console.log(result);
console.log("asyncCall end");
// expected output: 'resolved'
}
asyncCall();
console.log("loo");
下面这段代码,如果注释掉console.log(“lo”); ,其输出是hi,1。这个时候根本无法分辨这是同步还是异步。但是如果取消了注释,就变得有意思了,输出是hi,lo,1。这再次证明,await一个async函数,会导致这之后的代码被阻塞。
async function foo() {
console.log("hi");
return 1;
}
async function bar() {
const result = await foo();
console.log(result);
}
bar();
//注释掉,输出是hi,1。没有注释,输出是hi,lo,1。
//console.log("lo");
但是,局部的,仍然会阻塞。
async function foo() {
// 输出1
console.log("hi");
return 1;
}
async function bar() {
const result = await foo();
// 输出4:虽然result是处于Resolve状态,但是却是最后输出(局部的)。
console.log(result);
return 2;
}
let ansBar = bar();
// 输出2,全局的,“1”处于resolve状态,立刻输出。
console.log(ansBar);
// 输出3
console.log("lo");
setTimeout不会阻塞主线程的UI。但是没有setTimeout的方式,会阻塞。
https://stackoverflow.com/questions/32380344/async-update-dom-from-promises
function test(i){
return Promise.resolve()
.then(function() {
// update the DOM
setTimeout(function() {
document.getElementById("micro-out-div").innerHTML += i;
}, 0);
return i;
});
}
function testBlock(i){
return Promise.resolve()
.then(function() {
// update the DOM
document.getElementById("micro-out-div").innerHTML += i;
return i;
});
}
var loadSequence = [];
// loop through all the frames!
for (var i = 0; i < 29999; i++) {
//loadSequence.push(testBlock(i));
loadSequence.push(test(i));
}
Promise.all(loadSequence).then(function(){
window.console.log('all set...');
});
profileKernel(name: string, f: () => T | Tensor[]):
T {
let result: T|Tensor[];
const holdResultWrapperFn = () => {
result = f();
};
const timer = this.backendTimer.time(holdResultWrapperFn);
const results: Tensor[] =
Array.isArray(result) ? result : [result] as Tensor[];
results.forEach(r => {
const vals = r.dataSync();
util.checkComputationForErrors(vals, r.dtype, name);
timer.then(timing => {
let extraInfo = '';
if (timing.getExtraProfileInfo != null) {
extraInfo = timing.getExtraProfileInfo();
}
this.logger.logKernelProfile(name, r, vals, timing.kernelMs, extraInfo);
});
});
return result as T;
}
}