JavaScript 承诺如何在幕后运作
How JavaScript promises work behind the scenes
我对 promise 产生和消费时幕后发生的事情感到非常困惑。请澄清我的观点,抱歉我的英语不好。
- 空白对象是用 new 关键字创建的 Promise 构造函数是
called 和 new 关键字设置 Promise 构造函数指向的 this
空白对象 this = blankobject.
- Promise 构造函数在参数中接收回调(执行函数)
并调用执行函数。
- 执行器函数接收两个回调(解析、拒绝)作为参数
- setTimeout 在执行函数中被调用,setTimeOut 是
异步代码
- 异步代码进入后台,然后 Promise 构造函数 returns
Promise 对象以前的空白对象和 Promise 对象引用
已保存到 myPromise。
- 创建了一个变量
接下来会发生什么?当调用then
方法时then
方法的代码进入后台?我想象它进入后台并且变量是 console.log // 10
主代码执行完成后,异步代码启动 setTimeout
回调开始执行,执行完成后承诺得到履行并解析函数 returns 值。这个值如何存储在 promise 对象中以及 then
方法中发生了什么?
let myPromise = new Promise (
(resolve, reject) => {
setTimeout(() => {
console.log(getIDs)
resolve(10);
}, 1500);
}
)
let a = 10
myPromise.then(val => {
console.log(val);
})
console.log(a)
我将按执行顺序检查您的代码。
在任何时候,this
的值都是它开始时的值。那是因为你只使用了箭头函数。但这不相关,因为您没有引用 this
.
主要代码
let myPromise = new Promise(executor);
创建一个挂起的承诺对象。创建承诺时,将执行 executor
函数。
setTimeout(callback, 1500);
将 callback
函数放在某个内部计时器队列上。 javascript 引擎承诺在(至少)1500 毫秒后尽最大努力执行 callback
。
let a = 10;
将变量 a
设置为 10
。
myPromise.then(onFulfilled);
创建另一个未决承诺。它链接到 myPromise
,以便在 myPromise
完成时异步安排 onFulfilled
。
console.log(a);
打印 a
的值,即 10
.
接下来的 1500 毫秒没有任何反应。然后 callback
被执行。
setTimeout的回调
console.log(getIDs);
输出 getIDs
。从名字你可以猜到它是一个函数。所以像 [Function: getIDs]
这样的东西会被打印出来。
resolve(10);
满足 myPromise
并将其结果设置为 10
。由于 myPromised
现已完成,anotherPromise
的 onFulfilled
将被异步安排。
现在我们必须等待调用堆栈处理。之后,将调用onFulfilled
。
onFulfilled 的 myPromise.then
console.log(val);
打印 val
的内容。即myPromise
.
的结果
以下是内置Promise class的简化实现。 catch
和 finally
尚未实施。
提供给 Promise 构造函数的函数称为执行函数,并立即同步调用。
每个 promise 都有一个方法 .then
,可以实现 promise 的链接。
提供给 .then
的函数总是在微任务上异步调用(注意下面 queueMicrotask
的使用)。
每次调用 .then
时,都会创建并返回一个新的承诺。
.then
可以对同一个承诺多次调用,创建承诺结果的多播,以及承诺链的分支。
承诺可以处于以下三种状态之一:未决、已履行或已拒绝。状态转换是单向的:您不能从已完成或已拒绝返回到未决状态。
如果一个 promise 与另一个 promise 一起解决,那么两个 promise 链就会连接起来,外部 promise 将成为内部 promise 的状态(可能是待处理的),直到内部 promise 解决。
function Promise(executor) {
if (!executor) throw "Promise executor undefined"
let status = "pending", value, thenQ = []
const then = onFulfilled => {
let resolver
// This ensures control does not move to later promises
// until prior promises have been resolved.
const nextPromise = new Promise(resolve => (resolver = resolve))
// More than one "then" can be registered with each promise.
thenQ.push((...args) => resolver(onFulfilled(...args)))
return nextPromise
}
// We check if the result is a "thenable"; if so, we treat
// it as an inner promise, otherwise we simply fulfil with
// the result.
const resolve = result => result?.then ? result.then(fulfil) : fulfil(result)
// When a promise has been fulfilled, its "thens" can be run.
const fulfil = result => (status = "fulfilled", value = result, executeThens(value))
// "Thens" are run asynchronously, on a microtask.
const executeThens = value => queueMicrotask(() => thenQ.forEach(el => el(value)))
// The executor is run synchronously.
executor(resolve)
return {
then,
get status() { return status },
get value() { return value }
}
}
// Chaining
new Promise(resolve => {
console.log('Waiting for step 1...')
setTimeout(() => resolve("One, two..."), 1500)
})
.then(result => new Promise(resolve => {
console.log('Waiting for step 2...')
setTimeout(() => resolve(`${result}three, four`), 1500)
}))
.then(result => console.log(`Chaining result: ${result}.`))
// Branching
const p = new Promise(resolve => {
console.log('Waiting for step a...')
setTimeout(() => resolve("Alpha, Bravo..."), 1500)
})
p.then(result => new Promise(resolve => {
console.log('Waiting for step b1...')
setTimeout(() => resolve(`${result}Charlie, Delta`), 1500)
})).then(console.log)
p.then(result => {
console.log('Waiting for step b2...')
return `${result}Echo, Foxtrot`
}).then(console.log)
参见 also。
我对 promise 产生和消费时幕后发生的事情感到非常困惑。请澄清我的观点,抱歉我的英语不好。
- 空白对象是用 new 关键字创建的 Promise 构造函数是 called 和 new 关键字设置 Promise 构造函数指向的 this 空白对象 this = blankobject.
- Promise 构造函数在参数中接收回调(执行函数) 并调用执行函数。
- 执行器函数接收两个回调(解析、拒绝)作为参数
- setTimeout 在执行函数中被调用,setTimeOut 是 异步代码
- 异步代码进入后台,然后 Promise 构造函数 returns Promise 对象以前的空白对象和 Promise 对象引用 已保存到 myPromise。
- 创建了一个变量
接下来会发生什么?当调用then
方法时then
方法的代码进入后台?我想象它进入后台并且变量是 console.log // 10
主代码执行完成后,异步代码启动 setTimeout
回调开始执行,执行完成后承诺得到履行并解析函数 returns 值。这个值如何存储在 promise 对象中以及 then
方法中发生了什么?
let myPromise = new Promise (
(resolve, reject) => {
setTimeout(() => {
console.log(getIDs)
resolve(10);
}, 1500);
}
)
let a = 10
myPromise.then(val => {
console.log(val);
})
console.log(a)
我将按执行顺序检查您的代码。
在任何时候,this
的值都是它开始时的值。那是因为你只使用了箭头函数。但这不相关,因为您没有引用 this
.
主要代码
let myPromise = new Promise(executor);
创建一个挂起的承诺对象。创建承诺时,将执行 executor
函数。
setTimeout(callback, 1500);
将 callback
函数放在某个内部计时器队列上。 javascript 引擎承诺在(至少)1500 毫秒后尽最大努力执行 callback
。
let a = 10;
将变量 a
设置为 10
。
myPromise.then(onFulfilled);
创建另一个未决承诺。它链接到 myPromise
,以便在 myPromise
完成时异步安排 onFulfilled
。
console.log(a);
打印 a
的值,即 10
.
接下来的 1500 毫秒没有任何反应。然后 callback
被执行。
setTimeout的回调
console.log(getIDs);
输出 getIDs
。从名字你可以猜到它是一个函数。所以像 [Function: getIDs]
这样的东西会被打印出来。
resolve(10);
满足 myPromise
并将其结果设置为 10
。由于 myPromised
现已完成,anotherPromise
的 onFulfilled
将被异步安排。
现在我们必须等待调用堆栈处理。之后,将调用onFulfilled
。
onFulfilled 的 myPromise.then
console.log(val);
打印 val
的内容。即myPromise
.
以下是内置Promise class的简化实现。 catch
和 finally
尚未实施。
提供给 Promise 构造函数的函数称为执行函数,并立即同步调用。
每个 promise 都有一个方法 .then
,可以实现 promise 的链接。
提供给 .then
的函数总是在微任务上异步调用(注意下面 queueMicrotask
的使用)。
每次调用 .then
时,都会创建并返回一个新的承诺。
.then
可以对同一个承诺多次调用,创建承诺结果的多播,以及承诺链的分支。
承诺可以处于以下三种状态之一:未决、已履行或已拒绝。状态转换是单向的:您不能从已完成或已拒绝返回到未决状态。
如果一个 promise 与另一个 promise 一起解决,那么两个 promise 链就会连接起来,外部 promise 将成为内部 promise 的状态(可能是待处理的),直到内部 promise 解决。
function Promise(executor) {
if (!executor) throw "Promise executor undefined"
let status = "pending", value, thenQ = []
const then = onFulfilled => {
let resolver
// This ensures control does not move to later promises
// until prior promises have been resolved.
const nextPromise = new Promise(resolve => (resolver = resolve))
// More than one "then" can be registered with each promise.
thenQ.push((...args) => resolver(onFulfilled(...args)))
return nextPromise
}
// We check if the result is a "thenable"; if so, we treat
// it as an inner promise, otherwise we simply fulfil with
// the result.
const resolve = result => result?.then ? result.then(fulfil) : fulfil(result)
// When a promise has been fulfilled, its "thens" can be run.
const fulfil = result => (status = "fulfilled", value = result, executeThens(value))
// "Thens" are run asynchronously, on a microtask.
const executeThens = value => queueMicrotask(() => thenQ.forEach(el => el(value)))
// The executor is run synchronously.
executor(resolve)
return {
then,
get status() { return status },
get value() { return value }
}
}
// Chaining
new Promise(resolve => {
console.log('Waiting for step 1...')
setTimeout(() => resolve("One, two..."), 1500)
})
.then(result => new Promise(resolve => {
console.log('Waiting for step 2...')
setTimeout(() => resolve(`${result}three, four`), 1500)
}))
.then(result => console.log(`Chaining result: ${result}.`))
// Branching
const p = new Promise(resolve => {
console.log('Waiting for step a...')
setTimeout(() => resolve("Alpha, Bravo..."), 1500)
})
p.then(result => new Promise(resolve => {
console.log('Waiting for step b1...')
setTimeout(() => resolve(`${result}Charlie, Delta`), 1500)
})).then(console.log)
p.then(result => {
console.log('Waiting for step b2...')
return `${result}Echo, Foxtrot`
}).then(console.log)
参见 also。