一、Promise是什么

在 JavaScript 中,Promise 是一种处理异步操作的方式。Promise 可以让我们更加优雅地处理异步操作,而不需要使用回调函数嵌套。

● Promise 是一门新的技术(ES6 规范),是 JS 中进行异步编程的新解决方案(旧方案是单纯使用回调函数,容易形成回调地狱)

● 常见异步操作:①fs 文件操作 ②数据库操作 ③Ajax ④定时器

● 具体表达:

  1. 从语法上看:Promise是一个构造函数 (自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法)
  2. 从功能上看:promise对象用来封装一个异步操作并可以获取其成功/失败的结果值

二、创建 Promise

创建一个 Promise 非常简单,只需要使用 Promise 构造函数即可。Promise 构造函数接受一个函数作为参数,这个函数叫做执行器函数(executor function)。执行器函数有两个参数:resolvereject。当异步操作成功时,调用 resolve,并传递结果值作为参数;当异步操作失败时,调用 reject,并传递错误对象作为参数。

const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(reason);
  }
});

三、使用 Promise

使用 Promise 有两种方式:then 方法和 catch 方法。

1.then 方法

then 方法用于注册 Promise 成功时的回调函数。当 Promise 成功时,then 方法会被调用,并传递 Promise 成功时的结果值作为参数。

promise.then((result) => {
  // 处理 Promise 成功时的结果值
});

2.catch 方法

catch 方法用于注册 Promise 失败时的回调函数。当 Promise 失败时,catch 方法会被调用,并传递 Promise 失败时的错误对象作为参数。

promise.catch((error) => {
  // 处理 Promise 失败时的错误对象
});

四、Promise.resolve和Promise.reject

1.Promise.resolve 方法

在 JavaScript 中,Promise.resolve 是一个方法,它返回一个 Promise 对象,该对象以给定的值解析。如果该值本身是一个 Promise,则返回该 Promise;否则,返回一个以该值解析的新 Promise 对象。它通常用于将非 Promise 值转换为 Promise 对象,以便在 then 方法中使用。

Promise.resolve(value):

value:将被 Promise 对象解析的参数,也可以是一个成功或失败的 Promise 对象

返回一个带着给定值解析过的 Promise 对象,如果参数本身就是一个 Promise 对象,则直接返回这个 Promise 对象

  • 1.如果传入的参数为 非Promise类型的对象, 则返回的结果为成功promise对象
let p1 = Promise.resolve(521);
console.log(p1); // Promise {<fulfilled>: 521}
  • 2.如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {
// resolve('OK'); // 成功的Promise
reject('Error');
}));
console.log(p2);
p2.catch(reason => {
console.log(reason);
})

2.Promise.reject 方法

Promise.reject(reason):

reason:失败的原因

说明:返回一个失败的 promise 对象

let p = Promise.reject(521);
let p2 = Promise.reject('iloveyou');

let p3 = Promise.reject(new Promise((resolve, reject) => {
resolve('OK');
}));

console.log(p);
console.log(p2);
console.log(p3);   //reject 返回的promise调用的是resolve

● Promise.resolve()/Promise.reject() 方法就是一个语法糖,用来快速得到Promise对象

五、Promise 的状态和状态转换

Promise 有三种状态:

pending(进行中)、fulfilled(已成功)和 rejected(已失败)。Promise 的状态只能从 pending 转换为 fulfilledrejected,一旦状态转换完成,就不能再次转换。

下面是一个简单的例子,展示了 Promise 的状态和状态转换。

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('result');
  }, 1000);
});

console.log(promise); // Promise { <pending> }

promise.then((result) => {
  console.log(result); // result
  console.log(promise); // Promise { <fulfilled>:'result' }
});

console.log(promise); // Promise { <pending> }

在上面的例子中,Promise 的初始状态为 pending,因为异步操作需要 1 秒钟才能完成。当异步操作完成并成功时,Promise 状态转换为 fulfilled。在 then 方法中,我们可以访问 Promise 的结果值。最后,我们再次打印 Promise 对象,可以看到它的状态已经变为 fulfilled

如果异步操作失败,我们可以调用 reject 方法,将 Promise 状态转换为 rejected

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error('error'));
  }, 1000);
});

promise.catch((error) => {
  console.log(error); // Error: error
    console.log(promise); // Promise { <rejected>: Error: error }
});

在上面的例子中,异步操作失败,并且 Promise 状态转换为 rejected。在 catch 方法中,我们可以访问 Promise 的错误对象。

六、Promise的几个关键问题

💡 a、如何改变 promise 的状态?

1.resolve(value):如果当前是 pending 就会变为 resolved

2.reject(reason):如果当前是 pending 就会变为 rejected

3.抛出异常:如果当前是 pending 就会变为 rejected

💡 b、一个 promise 指定多个成功/失败回调函数,都会调用吗?

当 promise 改变为对应状态时都会调用

const p = new Promise((resolve, reject) => {
//resolve(1)
reject(2)
})
p.then(
value => {},
reason => {console.log('reason',reason)}
)
p.then(
value => {},
reason => {console.log('reason2',reason)}
)
// reason 2
// reason2 2

💡 c、promise.then() 返回的新 promise 的结果状态由什么决定?

(1)简单表达:由 then() 指定的回调函数执行的结果决定

(2)详细表达:

① 如果抛出异常,新 promise 变为 rejected,reason 为抛出的异常

② 如果返回的是非 promise 的任意值,新 promise 变为 resolved,value 为返回的值

③ 如果返回的是另一个新 promise,此 promise 的结果就会成为新 promise 的结果

💡 d、promise 如何串联多个操作任务?

(1)promise 的 then() 返回一个新的 promise,可以并成 then() 的链式调用
(2)通过 then 的链式调用串联多个同步/异步任务

new Promise((resolve, reject) => {
setTimeout(() => {
console.log('执行任务1(异步)')
resolve(1)
}, 1000)
}).then(
value => {
console.log('任务1的结果', value)
console.log('执行任务2(同步)')
return 2 // 同步任务直接return返回结果
}
).then(
value => {
console.log('任务2的结果', value)
return new Promise((resolve, reject) => { // 异步任务需要包裹在Promise对象中
setTimeout(() => {
console.log('执行任务3(异步)')
resolve(3)
}, 1000)
})
}
).then(
value => {
console.log('任务3的结果', value)
}
)
// 执行任务1(异步)
// 任务1的结果 1
// 执行任务2(同步)
// 任务2的结果 2
// 执行任务3(异步)
// 任务3的结果 3

💡 e、Promise 异常穿透(传透)?

(1)当使用 promise 的 then 链式调用时,可以在最后指定失败的回调

(2)前面任何操作出了异常,都会传到最后失败的回调中处理

new Promise((resolve, reject) => {
//resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2
}
).then(
value => {
console.log('onResolved2()', value)
return 3
}
).then(
value => {
console.log('onResolved3()', value)
}
).catch(
reason => {
console.log('onRejected1()', reason)
}
)
// onRejected1() 1

失败的结果是一层一层处理下来的,最后传递到 catch 中

七、Promise.all 和 Promise.race

除了基础的 Promise,ES6 还提供了两个静态方法:Promise.allPromise.race。这两个方法可以让我们更加方便地处理多个 Promise 实例。

1.Promise.all

Promise.all 方法用于将多个 Promise 实例包装成一个新的 Promise 实例。当所有 Promise 实例都成功时,新的 Promise 实例才会成功;当任意一个 Promise 实例失败时,新的 Promise 实例就会失败。

const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);

Promise.all([promise1, promise2]).then((results) => {
  console.log(results); // [1, 2]
});

在上面的例子中,Promise.all 方法接受一个 Promise 实例数组作为参数,返回一个新的 Promise 实例。当所有 Promise 实例都成功时,新的 Promise 实例会成功,并传递所有 Promise 实例的结果值数组作为参数。

2.Promise.race

Promise.race 方法用于将多个 Promise 实例包装成一个新的 Promise 实例。当其中请求最快的那个 Promise 实例成功或失败时,新的 Promise 实例就会成功或失败。

const promise1 = new Promise((resolve) => {
  setTimeout(() => {
    resolve(1);
  }, 1000);
});

const promise2 = new Promise((resolve) => {
  setTimeout(() => {
    resolve(2);
  }, 2000);
});

Promise.race([promise1, promise2]).then((result) => {
  console.log(result); // 1
});

在上面的例子中,Promise.race 方法接受一个 Promise 实例数组作为参数,返回一个新的 Promise 实例。当其中请求最快一个 Promise 实例成功或失败时,新的 Promise 实例就会成功或失败,并传递第一个成功或失败的 Promise 实例的结果值或错误对象作为参数。

通俗的讲,就是说你可能传给我了好几个异步,我都会去请求,但是只要有一个率先完成请求我就把他的结果(成功/失败)返回给你。至于剩下的请求,我只帮你请求,他是对是错我就不管了,我只关注最快的那个。

八、Promise应用场景

1.避免回调地狱

Promise 可以让我们更加优雅地处理异步操作,避免回调地狱。在使用 Promise 时,我们应该尽量避免使用回调函数嵌套,而是使用 Promise 链式调用的方式。

asyncFunc1()
  .then((result1) => {
    return asyncFunc2(result1);
  })
  .then((result2) => {
    return asyncFunc3(result2);
  })
  .then((result3) => {
    console.log(result3);
  })
  .catch((error) => {
    console.log(error);
  });

2.处理错误

在使用 Promise 时,我们应该尽可能地处理错误。使用 catch 方法可以处理 Promise 的错误,避免出现未处理的错误。

asyncFunc()
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.log(error);
  });

3.返回 Promise

在编写函数时,如果函数内部有异步操作,我们应该返回一个 Promise 实例,以便外部代码可以使用 Promise 的方式处理异步操作。

function asyncFunc() {
  return new Promise((resolve, reject) => {
    // 异步操作
    // 成功时调用 resolve(result)
    // 失败时调用 reject(error)
  });
}

九、手写 Promise 实现原理

class MyPromise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.onResolvedCallbacks.forEach((fn) => fn());
      }
    };

    const reject = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    };

    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
    onRejected = typeof onRejected === 'function' ? onRejected : (reason) => { throw reason; };

    const promise2 = new MyPromise((resolve, reject) => {
      if (this.state === 'fulfilled') {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      }

      if (this.state === 'rejected') {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      }

      if (this.state === 'pending') {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value);
              this.resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });

        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason);
              this.resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
      }
    });

    return promise2;
  }

  resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
      return reject(new TypeError('Chaining cycle detected'));
    }

    let called = false;

    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
      try {
        const then = x.then;

        if (typeof then === 'function') {
          then.call(x, (y) => {
            if (called) return;
            called = true;
            this.resolvePromise(promise2, y, resolve, reject);
          }, (r) => {
            if (called) return;
            called = true;
            reject(r);
          });
        } else {
          resolve(x);
        }
      } catch (error) {
        if (called) return;
        called = true;
        reject(error);
      }
    } else {
      resolve(x);
    }
  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }

  static resolve(value) {
    return new MyPromise((resolve) => {
      resolve(value);
    });
  }

  static reject(reason) {
    return new MyPromise((_, reject) => {
      reject(reason);
    });
  }

  static all(promises) {
    return new MyPromise((resolve, reject) => {
      const results = [];
      let count = 0;

      for (let i = 0; i < promises.length; i++) {
        promises[i].then((result) => {
          results[i] = result;
          count++;

          if (count === promises.length) {
            resolve(results);
          }
        }, reject);
      }
    });
  }

  static race(promises) {
    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(resolve, reject);
      }
    });
  }
}

在上面的代码中,我们创建了一个 MyPromise 类,它包含了 Promise 的基本实现。我们实现了 Promise 的基本功能,包括状态的改变、then 方法和 catch 方法的实现、Promise 的链式调用、错误处理和静态方法 resolverejectallrace 的实现。

十、总结

Promise 是一种处理异步操作的方式,它可以让我们更加优雅地处理异步操作。在使用 Promise 时,我们应该遵循一些最佳实践,包括避免回调地狱、处理错误和返回 Promise。希望这篇文章能够帮助你更好地使用 Promise。


某亿
1 声望5 粉丝

小鸡虽小,可它玩的是整个大地