数据类型

instanceof

实现方式:原型链检测是否是某个构造函数的实例,在原型链的尽头是Object.prototype,这是所有对象的原型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function myInstanceOf(obj,constructor){
if(obj===null||typeof(obj)!=='object'||typeof(constructor)!=='function'){
return false;
}
let proto=Object.getPrototypeOf(obj);
while(proto!==null){
if(proto===constructor){
return true;
}
proto=Object.getPrototypeOf(ptroto);
}
return false;

}

当你使用 instanceof 操作符检测一个对象是否是某个构造函数的实例时,实际上是在沿着对象的原型链向上查找,直到找到构造函数的原型或者原型链的尽头。

举个例子:

1
2
3
4
5
function Person() {}
const person = new Person();

console.log(person instanceof Person); // true
console.log(person instanceof Object); // true,因为所有对象都是 Object 的实例

在这个示例中,person 对象的原型链是这样的:

1
person --> Person.prototype --> Object.prototype --> null

Person.prototypeperson 对象的构造函数 Person 的原型,而 Object.prototypePerson.prototype 的原型,最终指向 null,这就是原型链的尽头。

函数

防抖

节流

深拷贝

事件循环

111

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
async function exampleAsync() {
console.log("Start");

const promise = new Promise((resolve, reject) => {

});

await promise;
console.log("End");
}

exampleAsync();

console.log("Start");

const promise = new Promise((resolve, reject) => {

});

promise.then(() => {
console.log("Promise resolved");
});

console.log("End");

算法

快速排序

111

useEffect

你给出的代码是一个使用React Hooks的简单组件示例。这个组件展示了如何使用useStateuseEffect来管理状态和副作用。下面,我会对这段代码进行解释,并翻译成中文注释。

组件功能解释:

  1. useState使用

    • useState(0) 创建一个状态变量count和一个更新这个状态的函数setCount。初始值为0
  2. useEffect使用

    • useEffect是用来处理组件的副作用(如数据获取、订阅等)。这里的副作用是打印当前的count值。
    • 每当count变化时,副作用会重新执行。首先执行副作用函数,然后在执行下一个副作用前,先执行清理函数。
  3. 渲染行为

    • 组件渲染一个h3元素,显示count的值。
    • 点击h3元素时,会通过setCount函数更新count值,增加1。由于countuseEffect的依赖项,这会导致组件重新渲染并触发副作用。

这个组件展示了React Hooks的基本用法,特别是状态管理和副作用的处理,使得组件逻辑更清晰和模块化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function App() {
// 使用useState钩子函数创建一个名为count的状态变量,初始值为0
const [count, setCount] = useState(0);

// 使用console.log打印当前渲染时的count值
console.log('渲染', count);

// 使用useEffect钩子函数处理副作用,依赖项是[count]
useEffect(() => {
// 当count更新后,控制台打印当前的count值
console.log('执行effect', count);
return () => {
// 组件卸载或count更新前,控制台打印上一个count的值
console.log('清理effect', count);
};
}, [count]);

// 渲染一个h3标签,显示当前的count值,点击时通过setCount更新count值
return (
<h3 onClick={() => setCount(count => count + 1)}>{count}</h3>
);
}

树结构输出父节点

打印value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
function printValuesBFS(root) {
let queue = [root];

while (queue.length > 0) {
const current = queue.shift();
console.log(current.value);

if (current.children) {
queue.push(...current.children);
}
}
}

// Example usage:
printValuesBFS(tree);
const tree = {
value: 1,
children: [
{
value: 2,
children: [
{
value: 3,
children: [],
},
],
},
{
value: 4,
children: [
{
value: 5,
children: [],
},
],
},
{
value: 6,
children: [],
},
]
};

事件循环输出题

第1题

1
2
3
4
5
6
7
8
9
const newPromise1 = new Promise((resolve, reject) => {
console.log('A');
resolve('B');
});
const newPromise2 = newPromise1.then(res => {
console.log(res);
});
console.log('C', newPromise1);
console.log('D', newPromise2);

执行流程

  1. 创建 newPromise1
    - new Promise() 构造函数立即执行,输出 A
    - resolve('B') 被调用,将 Promise 的状态从 pending 转变为 fulfilled,并传递值 'B' 作为 res 的值。

  2. 执行 newPromise1.then()

    • newPromise1 是一个已完成(fulfilled)的 Promise,所以 .then() 里的回调函数会在下一个微任务队列(microtask queue)执行,输出 B
  3. 打印 newPromise1newPromise2

    • console.log('C', newPromise1) 打印的是 newPromise1,此时它的状态已经是 fulfilled,值为 'B'
    • console.log('D', newPromise2) 打印的是 newPromise2,这是通过 .then() 返回的 Promise。由于 newPromise2 还在等待 newPromise1.then() 的回调执行,它的状态暂时为 pending,然后会变为 fulfilled,但控制台会显示它当前的状态为 pending

输出顺序:

  1. 'A' 是同步代码,立即输出。
  2. console.log('C', newPromise1) 输出 C 和一个已完成的 Promise,值为 'B'
  3. console.log('D', newPromise2) 输出 D 和一个处于 pending 状态的 Promise。
  4. 回调函数 console.log(res) 在事件循环的微任务队列执行,输出 'B'

最终输出结果:

1
2
3
4
A
C Promise { 'B' }
D Promise { <pending> }
B

注意:B 是在微任务队列中执行的,所以会在所有同步代码(包括 console.log('C', newPromise1)console.log('D', newPromise2)) 之后输出。

第2题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const newPromise = new Promise((resolve, reject) => {
console.log('A');
setTimeout(() => {
console.log("timer start");
resolve("succeed");
console.log("timer end");
}, 0);
console.log('B');
});

newPromise.then((result) => {
console.log(result);
});

console.log('C');

执行流程:

  1. 创建 newPromise

    • new Promise() 被调用时,执行器函数立即执行,首先同步执行 console.log('A'),输出:
      1
      A
    • 然后 setTimeout() 被设置为 0 毫秒后异步执行。setTimeout 本身是异步的,所以它会将回调函数放入事件队列,并继续往下执行。
    • 接着执行 console.log('B'),输出:
      1
      B
  2. 执行 newPromise.then()

    • 此时 Promise 还处于 pending 状态,因为 resolve() 尚未被调用。
    • newPromise.then() 中的回调函数被放入微任务队列,等待 Promise 变为 fulfilled 后执行。
  3. 执行 console.log('C')

    • console.log('C') 是同步代码,立即执行,输出:
      1
      C
  4. 处理 setTimeout 回调:

    • 当事件循环到达定时器回调时,setTimeout() 的回调函数被执行,输出:
      1
      timer start
    • 调用 resolve("succeed"),Promise 的状态变为 fulfilled,并且 then() 回调准备执行。
    • 接着输出 console.log("timer end")
      1
      timer end
  5. 执行 newPromise.then() 的回调:

    • 在事件循环的微任务队列中,then() 回调会执行,输出 resolve() 传递的值:
      1
      succeed

输出顺序总结:

  1. A 立即输出。
  2. B 立即输出。
  3. C 立即输出。
  4. 定时器的回调被异步执行,输出 timer starttimer end
  5. 最后,Promise 被 resolve,输出 succeed

最终输出结果:

1
2
3
4
5
6
A
B
C
timer start
timer end
succeed

第3题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Promise.resolve().then(() => {
console.log('outerPromise');
const innerTimer = setTimeout(() => {
console.log('innerTimer')
}, 0);
});

const timer1 = setTimeout(() => {
console.log('outerTimer');
Promise.resolve().then(() => {
console.log('innerPromise');
});
}, 0);

console.log('run');

执行流程:

1. 同步代码部分

  • Promise.resolve() 会立即创建一个已完成的 Promise,并且将 .then() 的回调放入 微任务队列
  • setTimeout() 将回调放入 宏任务队列,等待主线程的同步代码执行完成后执行。
  • console.log('run') 是同步代码,立即执行。

2. 微任务队列执行顺序

  • 在同步代码执行完之后,事件循环首先会处理 微任务队列
  • Promise.resolve().then(() => {...}) 的回调会首先执行,因为它是放入 微任务队列 中的。这会输出 outerPromise

3. 宏任务队列执行顺序

  • 微任务执行完成后,事件循环会开始处理 宏任务队列
  • 定时器回调(通过 setTimeout 注册的任务)会依次执行。
  • setTimeout(() => {...}, 0) 的回调函数 console.log('outerTimer') 被执行。
  • Promise.resolve().then(() => {...}) 再次将其 .then() 回调放入 微任务队列,输出 innerPromise

4. 内层定时器的执行

  • 内层定时器 setTimeout(() => { console.log('innerTimer') }, 0) 也在 宏任务队列 中,它会在外层的宏任务执行完毕后执行。

输出顺序:

  1. 同步代码部分:首先执行同步代码,输出 run
  2. 微任务部分:在同步代码执行完毕后,执行微任务队列中的 Promise.resolve() 回调,输出 outerPromise
  3. 外层定时器:然后执行 timer1 的回调,输出 outerTimer,紧接着再将内层 Promise.resolve().then() 放入微任务队列。
  4. 微任务部分:微任务队列中有 innerPromise 的输出,所以输出 innerPromise
  5. 最后执行内层定时器:最后执行宏任务队列中内层定时器的回调,输出 innerTimer

最终输出顺序:

1
2
3
4
5
run
outerPromise
outerTimer
innerPromise
innerTimer

事件循环顺序回顾:

  1. 同步代码先执行(输出 run)。
  2. 处理微任务队列(输出 outerPromise)。
  3. 处理宏任务队列中的定时器(输出 outerTimer 和之后的 innerPromise)。
  4. 执行宏任务中的剩余定时器(输出 innerTimer)。

第4题

在这段代码中,Promise 的行为可能会引发一些疑问。让我们逐步分析它的执行流程:

1
2
3
4
5
6
7
8
9
10
11
const promise = new Promise((resolve, reject) => {
resolve('succeed1');
reject('error');
resolve('succeed2');
});

promise.then((res) => {
console.log('then: ', res);
}).catch((err) => {
console.log('catch: ', err);
});

执行流程解析:

  1. Promise 构造函数的执行

    • 一旦 new Promise 被调用,传递的执行器函数立即开始执行。
    • 第一个 resolve('succeed1') 调用了 resolve,这将把 promise 的状态从 pending 改变为 fulfilled,并将值设为 'succeed1'。一旦 Promise 的状态变为 fulfilledrejected,它的状态就不可再改变。
    • reject('error')resolve('succeed2')resolve('succeed1') 之后被调用,但由于 Promise 状态一旦从 pending 转变为其他状态(无论是 fulfilled 还是 rejected),后续的状态变更请求将被忽略。因此,这两个操作不会产生任何效果。
  2. thencatch 的执行

    • promise.then() 会被调用,因为 promise 的状态已经是 fulfilled,并且值为 'succeed1'
    • .then() 的回调会打印出 then: succeed1
    • 由于 Promise 已经是成功的状态,.catch() 不会被触发。

为什么 reject('error')resolve('succeed2') 被忽略?

  • Promise 中,**一旦 resolvereject 其中之一被调用,状态就锁定为 fulfilledrejected**。任何后续对 resolvereject 的调用都会被忽略。因此,尽管 reject('error')resolve('succeed2') 也被调用了,但它们都没有实际效果,因为 Promise 状态在第一次 resolve('succeed1') 调用后已经被锁定。

最终输出:

1
then:  succeed1

总结:

  • resolvereject 只会生效一次,后续的调用都会被忽略。
  • 代码中的 resolve('succeed1') 成功后,then 回调被执行,并输出 'succeed1',后面的 reject 和第二个 resolve 调用无效。
  • catch 永远不会被触发,因为 Promise 的状态是成功的(fulfilled)。

第5题

在这段代码中,Promise.then() 方法看起来有些特殊,因为你在 .then() 中直接传递了非函数值。那么,这会导致什么样的行为?让我们逐步分析:

1
2
3
4
Promise.resolve('A')
.then('B') // 这里传递了一个非函数值
.then(Promise.resolve('C')) // 这里传递了一个 Promise 对象
.then(console.log);

关键点:

  1. Promise.then() 的参数必须是一个函数
    • 如果 .then() 中传递的不是一个函数(例如 'B'Promise.resolve('C')),它会被忽略,默认传递原始值下去,等效于:
      1
      .then(value => value)
    • 这意味着非函数值 BPromise.resolve('C') 会被视为普通值,并且没有特殊处理。

执行流程分析:

  1. **Promise.resolve('A')**:

    • 这行代码会立即返回一个已完成状态(fulfilled)的 Promise,值为 'A'
  2. **then('B')**:

    • 由于 'B' 不是一个函数,它会被忽略。.then() 会将前一个 Promise 的值 'A' 直接传递到下一个 .then()
  3. **then(Promise.resolve('C'))**:

    • Promise.resolve('C') 是一个已完成的 Promise,但是这里 .then() 接受的是值而不是函数。由于 Promise.resolve('C') 本身是一个对象(而不是函数),所以它也会被忽略。then() 还是会把上一个 Promise 的值 'A' 传递到下一个 .then()
  4. **then(console.log)**:

    • 最终,这里会执行 console.log('A'),因为值 'A' 被传递了下来。

最终输出:

1
A

关键总结:

  • then() 中传递的非函数参数会被忽略。这意味着 'B'Promise.resolve('C') 都不会影响结果。
  • .then() 只是简单地传递上一个 Promise 的结果,最终输出 A

第6题

让我们一步步分析这段代码的执行流程,重点是 Promise 的状态变化以及控制台输出的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})

const promise2 = promise1.then(() => {
throw new Error('error')
})

console.log('promise1', promise1)
console.log('promise2', promise2)

setTimeout(() => {
console.log('innerPromise1', promise1)
console.log('innerPromise2', promise2)
}, 2000)

分析步骤:

  1. **创建 promise1**:

    • promise1 是一个新的 Promise,并且由于 setTimeout 还没有触发,promise1 在创建时处于 pending(待定)状态
  2. **创建 promise2**:

    • promise2 是由 promise1.then() 创建的新的 Promise。此时,promise1 仍然处于 pending 状态,并且 promise2 也同样处于 pending 状态。
    • .then() 的回调还没有执行,因为 promise1 还没有 resolve
  3. **第一次输出 promise1promise2**:

    • 在这一步,promise1promise2 都还处于 pending 状态,因为 setTimeout 的延迟还没有达到,因此控制台会输出:
    1
    2
    promise1 Promise { <pending> }
    promise2 Promise { <pending> }
  4. **promise1 在 1 秒后变为 fulfilled**:

    • 在 1 秒之后,setTimeout 回调触发,resolve('success') 被调用,因此 promise1 的状态会变为 fulfilled,值为 'success'
    • 但是,promise1.then() 的回调会抛出错误 Error('error'),因此 promise2 的状态会变为 rejected,并且值是这个错误。
  5. **第二次输出 promise1promise2**(在 2 秒后):

    • 当第二个 setTimeout 回调(2000 毫秒)执行时:
      • promise1 已经变为 fulfilled,值为 'success'
      • promise2 则因为 .then() 抛出了错误,状态变为 rejected,值是这个错误。

    因此,在第二次输出中,控制台会显示:

    1
    2
    innerPromise1 Promise { 'success' }
    innerPromise2 Promise { <rejected> Error: error }

详细的输出顺序:

  1. 在最初输出时(在 setTimeout 触发之前):

    1
    2
    promise1 Promise { <pending> }
    promise2 Promise { <pending> }
  2. 在第二次输出时(在 2 秒后):

    1
    2
    innerPromise1 Promise { 'success' }
    innerPromise2 Promise { <rejected> Error: error }

总结:

  • promise1 在 1 秒后会变为 fulfilled,值为 'success'
  • promise2 会在 promise1.then() 中因为抛出了错误,变为 rejected,并且这个错误会成为它的值。
  • 初次输出时,两个 Promise 都是 pending 状态。
  • 1 秒后,promise1 变为 fulfilledpromise2 变为 rejected

第7题

让我们逐步分析这段代码的执行流程和最终输出:

1
2
3
4
5
6
7
8
9
10
11
Promise.resolve('A')
.then(res => {
console.log(res);
return 'B';
})
.catch(err => {
return 'C';
})
.then(res => {
console.log(res);
});

分析步骤:

  1. Promise.resolve('A'):

    • 这个创建了一个已完成的 Promise,值为 'A'
  2. 第一个 .then(res => {...}):

    • Promise.resolve('A') 状态是已完成(fulfilled),所以会调用第一个 .then() 中的回调函数。
    • res 的值是 'A',因此输出:
      1
      A
    • 回调函数的返回值是 'B',这个值将传递给下一个 .then(),因此接下来链中的 Promise 也会以值 'B' 变为 fulfilled
  3. .catch(err => {...}):

    • .catch() 只会在前面的 Promise 被拒绝(rejected)时执行。
    • 因为前一个 .then() 成功并返回了 'B',所以 .catch() 不会被执行
  4. 第二个 .then(res => {...}):

    • 由于 .catch() 没有被执行,前一个 Promise 的值 'B' 会传递到第二个 .then() 中。
    • 因此,这里 res 的值是 'B',输出:
      1
      B

最终输出顺序:

1
2
A
B

总结:

  • 第一个 .then() 接收到 'A',输出 'A' 并返回 'B'
  • .catch() 不会被触发,因为前面的操作没有抛出错误。
  • 第二个 .then() 接收到 'B',输出 'B'

最终结果是:AB

8

1
2
3
4
5
6
7
Promise.resolve().then(() => {
return new Error('error')
}).then(res => {
console.log("then: ", res)
}).catch(err => {
console.log("catch: ", err)
})

关键点:

  • 返回 Error 对象:在 Promisethen() 回调中,返回一个普通的值(无论是对象、字符串,还是其他类型),Promise 会把它当作一个正常的返回值,而不是抛出异常。
  • 区别于抛出异常:如果你是抛出一个 Error(通过 throw),那么 Promise 会进入 rejected 状态;但如果你只是返回 Error 对象,它被视为一个普通的值,不会进入 catch

执行流程分析:

  1. **Promise.resolve()**:

    • 这行代码立即创建并返回一个已完成(fulfilled)的 Promise,没有值。
  2. **第一个 .then()**:

    • 由于 Promise.resolve() 的状态是 fulfilled,它会执行第一个 .then() 的回调。
    • 该回调返回了一个 Error 对象,但这只是一个普通的返回值,不会被视为异常抛出,所以它被当作普通值传递给下一个 .then()
  3. **第二个 .then()**:

    • 上一个 .then() 返回的 Error 对象被传递到这里,res 的值就是这个 Error 对象。
    • 因此,控制台会输出:
      1
      then:  Error: error
  4. **.catch()**:

    • 因为没有任何 Promise 进入 rejected 状态,.catch() 不会被触发,因此不会执行。

最终输出:

1
then:  Error: error

总结:

  • Promise 中返回 Error 对象不会导致 Promise 进入 rejected 状态。
  • 返回的 Error 对象会像普通值一样被传递到下一个 .then(),因此输出 "then: Error: error"
  • .catch() 不会被触发,因为没有异常被抛出。

9

在这段代码中,Promise 的执行逻辑涉及到一些特殊的循环引用场景,让我们分析它的行为和结果:

1
2
3
4
const promise = Promise.resolve().then(() => {
return promise;
})
promise.catch(console.err)

代码解析:

  1. const promise = Promise.resolve().then(() => { return promise; }):

    • 首先,Promise.resolve() 立即返回一个已完成(fulfilled)的 Promise,并执行接下来的 .then() 方法。
    • .then() 的回调中,代码试图返回 promise 自己。这会导致一个循环引用的问题。
  2. 循环引用的后果:

    • 在 JavaScript 中,Promisethen() 不能返回其自身。这是因为返回 promise 自己会导致一种“无法解决的递归”:Promise 一直在等待自己被解决,从而永远无法完成。
    • 为防止这种循环依赖,JavaScript 规范明确规定,如果 Promise.then() 返回了其自身,会导致 TypeError,并使该 Promise 进入 rejected 状态。
  3. promise.catch(console.err):

    • 因为 .then() 中返回了 promise 自身,promise 会进入 rejected 状态,理由是 Promise 不能等待自身完成。
    • 但是在代码中,catch 方法的回调是 console.err,这实际上是一个错误的引用,console 的方法应该是 console.error
    • 如果 console.err 没有被定义,会抛出一个 TypeError,导致 catch() 回调本身出错。

执行过程的结果:

  • .then() 中返回 promise 自己时,Promise 会进入 rejected 状态并抛出一个 TypeError
  • 因为 catch(console.err) 中的 console.err 不是一个有效的方法名,执行时会报错,这本身是一个错误。

正确的修复方式:

  • 使用 console.error 以正确地捕捉和打印错误。
1
2
3
4
const promise = Promise.resolve().then(() => {
return promise;
})
promise.catch(console.error); // 这里使用 console.error 而不是 console.err

输出:

  • 代码中的 .then() 会导致 promise 进入 rejected 状态,并抛出一个 TypeError,这会被 catch() 捕获。
  • 使用正确的 console.error,输出会是类似这样的信息:
    1
    TypeError: Chaining cycle detected for promise #<Promise>

结论:

  • Promise.then() 中返回自身会导致递归循环,进而使 Promise 进入 rejected 状态。
  • 应该使用 console.error 而不是 console.err,以正确地捕获并输出错误消息。

10

让我们再一次分析这段代码的执行流程,重点关注 .then() 中非函数值的处理。

1
2
3
4
Promise.resolve('A')
.then('B')
.then(Promise.resolve('C'))
.then(console.log)

关键点:

  1. then() 的参数必须是一个函数
    • .then() 方法的参数通常是一个回调函数。如果传递的不是函数,比如一个值(如 'B'Promise.resolve('C')),它会被忽略,并且 .then() 会默认传递前一个 Promise 的结果。

执行流程分析:

  1. **Promise.resolve('A')**:

    • 这行代码立即创建一个 fulfilled 状态的 Promise,值为 'A'
  2. **then('B')**:

    • 因为 'B' 不是函数,then() 会忽略这个值,并将前一个 Promise 的结果 'A' 直接传递到下一个 then()
  3. **then(Promise.resolve('C'))**:

    • Promise.resolve('C') 是一个已经被解析的 Promise,但由于它被传递给了 then(),且 then() 期待的是函数,Promise.resolve('C') 会被忽略,仍然将之前的值 'A' 传递下去。
  4. **then(console.log)**:

    • 这里 console.log 是一个有效的函数,因此 then() 会调用它,并打印出之前传递下来的值 'A'

关键结论:

  • .then() 如果接收到的是一个非函数值,它会被忽略,默认继续传递之前的结果。
  • 所以最终,console.log 打印的值是 'A'

最终输出:

1
A

总结:

  • 非函数值传递给 .then() 会被忽略。
  • 尽管有 'B'Promise.resolve('C') 传递给 .then(),它们都没有影响结果,最终仍然是 'A' 被传递下来并输出。

11

让我们再一次分析这段代码的执行流程,重点关注 .then() 中非函数值的处理。

1
2
3
4
Promise.resolve('A')
.then('B')
.then(Promise.resolve('C'))
.then(console.log)

关键点:

  1. then() 的参数必须是一个函数
    • .then() 方法的参数通常是一个回调函数。如果传递的不是函数,比如一个值(如 'B'Promise.resolve('C')),它会被忽略,并且 .then() 会默认传递前一个 Promise 的结果。

执行流程分析:

  1. **Promise.resolve('A')**:

    • 这行代码立即创建一个 fulfilled 状态的 Promise,值为 'A'
  2. **then('B')**:

    • 因为 'B' 不是函数,then() 会忽略这个值,并将前一个 Promise 的结果 'A' 直接传递到下一个 then()
  3. **then(Promise.resolve('C'))**:

    • Promise.resolve('C') 是一个已经被解析的 Promise,但由于它被传递给了 then(),且 then() 期待的是函数,Promise.resolve('C') 会被忽略,仍然将之前的值 'A' 传递下去。
  4. **then(console.log)**:

    • 这里 console.log 是一个有效的函数,因此 then() 会调用它,并打印出之前传递下来的值 'A'

关键结论:

  • .then() 如果接收到的是一个非函数值,它会被忽略,默认继续传递之前的结果。
  • 所以最终,console.log 打印的值是 'A'

最终输出:

1
A

总结:

  • 非函数值传递给 .then() 会被忽略。
  • 尽管有 'B'Promise.resolve('C') 传递给 .then(),它们都没有影响结果,最终仍然是 'A' 被传递下来并输出。

12

让我们详细分析这段代码的执行流程,并明确每个步骤的输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Promise.resolve('A')
.then(res => {
console.log('promise1', res)
})
.finally(() => {
console.log('finally1')
})

Promise.resolve('B')
.finally(() => {
console.log('finally2')
return 'result'
})
.then(res => {
console.log('promise2', res)
})

执行流程解析:

第一部分:

1
2
3
4
5
6
7
Promise.resolve('A')
.then(res => {
console.log('promise1', res)
})
.finally(() => {
console.log('finally1')
})
  1. Promise.resolve('A'):

    • 这个语句会立即返回一个 fulfilled 状态的 Promise,值为 'A'
  2. 第一个 .then():

    • 因为 Promise.resolve('A') 是成功的,所以第一个 .then() 的回调函数会被执行。
    • res 的值为 'A',因此会输出:
      1
      promise1 A
  3. 第一个 .finally():

    • .finally() 总会执行,无论 Promise 是成功还是失败。
    • 所以接下来会执行 finally 回调,输出:
      1
      finally1

第二部分:

1
2
3
4
5
6
7
8
Promise.resolve('B')
.finally(() => {
console.log('finally2')
return 'result'
})
.then(res => {
console.log('promise2', res)
})
  1. Promise.resolve('B'):

    • 类似于第一部分,这行代码立即返回一个已完成的 Promise,值为 'B'
  2. 第二个 .finally():

    • 虽然 .finally() 回调返回了 'result',但它的返回值不会影响 Promise 链的继续,也不会改变 Promise 的值。
    • 所以会执行 finally2 回调,输出:
      1
      finally2
  3. 第二个 .then():

    • .then() 依旧会接收来自 Promise.resolve('B') 的值,即 'B',而不是 'finally' 返回的 'result'
    • 因此,输出:
      1
      promise2 B

最终输出顺序:

  1. promise1 A
  2. finally1
  3. finally2
  4. promise2 B

总结:

  • .finally() 的回调函数不改变 Promise 链中传递的值。即使在 finally2 中返回了 'result',它不会影响后续 .then() 中的值。
  • finally 回调总是执行的,无论 Promise 成功还是失败。

13

让我们逐步分析这段代码,特别是 Promise.all() 的行为和异步函数的执行顺序。

1
2
3
4
5
6
7
8
9
10
function runAsync(num) {
return new Promise(
r => setTimeout(
() => r(num, console.log(num)
), 1000)
)
}

Promise.all([runAsync(1), runAsync(2), runAsync(3)])
.then(res => console.log(res));

代码的执行流程:

1. runAsync 函数

  • runAsync 是一个返回 Promise 的函数,它的核心逻辑是:
    • 创建一个 Promise,并在 setTimeout 1秒后调用 resolve
    • resolve(num) 被调用时,还会立即执行 console.log(num),在输出 num 的同时,Promise 被解决(fulfilled)。

2. **Promise.all()**:

  • Promise.all([runAsync(1), runAsync(2), runAsync(3)]) 同时启动了 3 个 runAsync 函数,这些函数在 1 秒后几乎同时解决(fulfilled)。
  • Promise.all() 会等待所有 Promise 都解决,返回一个包含每个 Promise 解决值的数组,按传入的顺序排列。
  • 如果所有 Promise 成功,Promise.all() 返回一个 fulfilledPromise,并将每个 Promise 的返回值作为数组传递给 .then()

执行顺序:

  1. **runAsync(1)runAsync(2)runAsync(3)**:

    • 这三个函数几乎同时开始执行,它们各自启动一个 1 秒的计时器。
    • 计时器结束后,按照调用顺序,依次执行 console.log(num) 并解决各自的 Promise
    • 虽然 setTimeout 函数调用几乎同时开始,但 console.log 的输出顺序依赖于 runAsync 的参数。由于所有 PromisesetTimeout 时间相同,因此它们的 resolve 操作会在 1 秒后几乎同时执行。
  2. **Promise.all()**:

    • 等到所有 runAsync 函数完成后,Promise.all() 将这些 Promise 的结果按传入的顺序组合成一个数组,并传递给 .then()

执行结果分析:

  • console.log(num) 会在每个 Promise 被解决前调用,分别输出 1, 2, 3
  • Promise.all() 将等待所有 Promise 解决,并按传入顺序返回 [1, 2, 3].then()

最终输出顺序:

  1. 在 1 秒后,分别输出:

    1
    2
    3
    1
    2
    3
  2. Promise.all() 解决后,输出:

    1
    [1, 2, 3]

代码的最终输出:

1
2
3
4
1
2
3
[1, 2, 3]

总结:

  • Promise.all() 保证返回的数组结果与传入的 Promise 顺序一致。
  • 即使 console.log 在每个 Promise 内部被调用,Promise.all() 仍会按顺序返回每个 Promise 的结果。

14

15