JavaScript 承诺如何在幕后运作

How JavaScript promises work behind the scenes

我对 promise 产生和消费时幕后发生的事情感到非常困惑。请澄清我的观点,抱歉我的英语不好。

  1. 空白对象是用 new 关键字创建的 Promise 构造函数是 called 和 new 关键字设置 Promise 构造函数指向的 this 空白对象 this = blankobject.
  2. Promise 构造函数在参数中接收回调(执行函数) 并调用执行函数。
  3. 执行器函数接收两个回调(解析、拒绝)作为参数
  4. setTimeout 在执行函数中被调用,setTimeOut 是 异步代码
  5. 异步代码进入后台,然后 Promise 构造函数 returns Promise 对象以前的空白对象和 Promise 对象引用 已保存到 myPromise。
  6. 创建了一个变量

接下来会发生什么?当调用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 现已完成,anotherPromiseonFulfilled 将被异步安排。

现在我们必须等待调用堆栈处理。之后,将调用onFulfilled

onFulfilled 的 myPromise.then

console.log(val); 打印 val 的内容。即myPromise.

的结果

以下是内置Promise class的简化实现。 catchfinally 尚未实施。

提供给 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