Observables 和 Promises 对比区别

189 阅读3分钟

「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战

前言

在 JavaScript 中 Observables 和 Promises 都能够解决异步问题。那我们在开发的时候选择哪种会比较方便快捷呢,这次我们来对比一下他们之间的用法用什么区别。

区别

Promises 和 Observables 处理异步的方法完全不一样,主要表现在以下几点。

单值和多值

Promise 的状态一旦变成 fulfilled(已完成),就无法再次改变状态,这就导致了它只能触发(reject, resolve)一次值。而Observables能够触发多次值,直到观察者完成或者取消订阅,否则订阅者将一直接收结果。

// Promise
const promise = new Promise((resolve) => {
    // 只会触发第一次 resolve
    // 触发后状态不会再改变
    resolve(1)
    resolve(2)
    resolve(3)
});

而Observables是这样的:

const observable = Rx.Observable.create((observer) => {
  observer.next(1);
  observer.next(2);
  observer.next(3);
  observer.complete();
  observer.next(4);
})
observable.subscribe(
  v => console.log(v),
  err => console.log(err),
  done => console.log("completed")
);

image.png

可以看到直到 observer 已完成之前,订阅者可以一直接收消息。这使得Observables可以很好的接收流式数据。

Observable 订阅可以取消;Promise 不能取消

一旦我们开始了Promise,那么我们不能再取消它,Promise 会被它的回调函数使用resolve或reject进行响应。而Observable是可以取消的。一但订阅者被创建它可以随时被观察者移除。这样当用户离开页面时我们可以停止订阅。

// 可以使用 unsubscribe 停止订阅
const observable = Rx.Observable.create((observer) => {
    let i = 0;
    const token = setInterval(() => {
        observer.next(i++);
    }, 1000);
});
const subscription = observable.subscribe(value => console.log(value + '!'));

subscription.unsubscribe();

或者通过take 或 takeUntil 方法进行控制停止。

// 使用 take 
const observable = Rx.Observable.create((observer) => {
    let i = 0;
    const token = setInterval(() => {
        observer.next(i++);
    }, 1000);
});
const subscription = observable.take(5).subscribe(x => console.log(x));

执行顺序

Promise 会在创建的时候就执行构造函数的代码,而Observables会在subscribe时才会被执行。
Promise

const foo = new Promise((resolve) => {
  console.log('promise');
  resolve(true);
});
// 会直接打印出 promise

const observable = new Rx.Observable(observer => {
  console.log('1. Execution of observable body');
  observer.next(1);
  observer.complete();
});
// 创建时不会执行里面的代码,所以没有打印
observable.subscribe(() => {
  console.log('2. Execution of subscribe callback')
});
// 当执行 subscribe 后开始执行代码,输出顺序为
// 1. Execution of observable body
// 2. Execution of subscribe callback

运行时执行时机

在Promises中,一旦执行了resolved,将在微任务队列中排队回,也就是说他会在当前宏任务之后执行。比如:

console.log('1. Start');

setTimeout(() => {
    console.log('2. Timeout callback')
}, 0);

Promise.resolve().then(() => {
    console.log('3. Promise callback')
});

console.log('4. End');

// 1. Start
// 4. End
// 3. Promise callback
// 2. Timeout callback

我们无法改变这种行为。

在Observables中可以使用 Scheduler 控制订阅何时启动、通知何时发出的程序。先看下面这个例子,我们不使用 Scheduler 调度。

var observable = Rx.Observable.create(function (observer) {
  observer.next(1);
  observer.next(2);
  observer.next(3);
  observer.complete();
})

console.log('just before subscribe');
observable.subscribe({
  next: x => console.log('got value ' + x),
  complete: () => console.log('done'),
});
console.log('just after subscribe');

/** 打印顺序为:
just before subscribe
got value 1
got value 2
got value 3
done
just after subscribe
*/

我们可以使用 Scheduler 来改变执行顺序

var observable = Rx.Observable.create(function (observer) {
  observer.next(1);
  observer.next(2);
  observer.next(3);
  observer.complete();
}).observeOn(Rx.Scheduler.asap);

console.log('just before subscribe');
observable.subscribe({
  next: x => console.log('got value ' + x),
  complete: () => console.log('done'),
});
console.log('just after subscribe');
/** 打印顺序为:
just before subscribe
just after subscribe
got value 1
got value 2
got value 3
done
*/