Promise

未处理的 Error

在浏览器中,我们可以使用 unhandledrejection 事件捕获它:

  1. window.addEventListener('unhandledrejection', function(event) {
  2. // the event object has two special properties:
  3. alert(event.promise); // [object Promise] - the promise that generated the error
  4. alert(event.reason); // Error: Whoops! - the unhandled error object
  5. });
  6. new Promise(function() {
  7. throw new Error("Whoops!");
  8. }); // no catch to handle the error

“error-first callback” 风格

  1. loadScript('/my/script.js', function(error, script) {
  2. if (error) {
  3. // handle error
  4. } else {
  5. // 成功加载脚本
  6. }
  7. });
  8. function loadScript(src, callback) {
  9. let script = document.createElement('script');
  10. script.src = src;
  11. script.onload = () => callback(null, script);
  12. script.onerror = () => callback(new Error(`Script load error for ${src}`));
  13. document.head.append(script);
  14. }

promise 对象有内部属性:

  • state —— 最初是 “pending”,然后被改为 “fulfilled” 或 “rejected”,
  • result —— 一个任意值,最初是 undefined

reject/throw a error

从技术上来说,我们可以使用任何类型的参数来调用 reject(就像 resolve)。但建议在 reject(或从它们中继承)中使用 Error 对象。 错误原因就会显示出来。

Promise chain

因为 promise.then 返回了一个 promise,所以我们可以用它调用下一个 .then
当控制函数返回一个值时,它会变成当前 promise 的 result,所以会用它调用下一个 .then

  1. new Promise(function(resolve, reject) {
  2. setTimeout(() => resolve(1), 1000); // (*)
  3. }).then(function(result) { // (**)
  4. alert(result); // 1
  5. return result * 2;
  6. }).then(function(result) { // (***)
  7. alert(result); // 2
  8. return result * 2;
  9. }).then(function(result) {
  10. alert(result); // 4
  11. return result * 2;
  12. });

返回thenable对象


.then 可以返回任意的 “thenable” 对象,并且会被当做一个 promise 来对待

  1. class Thenable {
  2. constructor(num) {
  3. this.num = num;
  4. }
  5. then(resolve, reject) {
  6. alert(resolve); // function() { native code }
  7. // 1 秒后用 this.num*2 来 resolve
  8. setTimeout(() => resolve(this.num * 2), 1000); // (**)
  9. }
  10. }
  11. new Promise(resolve => resolve(1))
  12. .then(result => {
  13. return new Thenable(result); // (*)
  14. })
  15. .then(alert); // 1000 ms 后显示 2

then方法中返回 promises 允许我们建立异步动作链。

错误处理

当一个 promise reject 时,代码控制流程跳到链中最近的 rejection 处理程序。这在实践中非常方便。

隐式catch

  1. new Promise(function(resolve, reject) {
  2. throw new Error("Whoops!");
  3. }).catch(alert); // Error: Whoops!
  4. === 等于下面的
  5. new Promise(function(resolve, reject) {
  6. reject(new Error("Whoops!"));
  7. }).catch(alert); // Error: Whoops!

re-throw

如果我们在 .catch 里面 throw,那么控制流程将转到下一个最接近的错误处理程序。
如果我们处理错误并正常结束,那么它将继续执行最接近的 .then 成功处理程序。

Promise.resolve

根据给定的 value 值返回 resolved promise。
等价于:

  1. let promise = new Promise(resolve => resolve(value));

如何接口的一致性 可以使用loadCached(url).then这样子
确保接口返回都返回Promise

  1. function loadCached(url) {
  2. let cache = loadCached.cache || (loadCached.cache = new Map());
  3. if (cache.has(url)) {
  4. return Promise.resolve(cache.get(url)); // (*)
  5. }
  6. return fetch(url)
  7. .then(response => response.text())
  8. .then(text => {
  9. cache[url] = text;
  10. return text;
  11. });
  12. }

Promise.all

下面的 Promise.all 在 3 秒之后被处理,然后它的结果就是一个 [1, 2, 3] 数组

  1. Promise.all([
  2. new Promise((resolve, reject) => setTimeout(() => resolve(1), 3000)), // 1
  3. new Promise((resolve, reject) => setTimeout(() => resolve(2), 2000)), // 2
  4. new Promise((resolve, reject) => setTimeout(() => resolve(3), 1000)) // 3
  5. ]).then(alert);

如果任何 promise 为 rejected,Promise.all 就会立即以 error reject。

如何避免fetch请求中一个错误导致Promise.all失败

根据 promise 的工作原理,只要 .then/catch 处理器返回值(无论是 error 对象或其他内容),执行流程就会“正常”进行

  1. Promise.all(
  2. urls.map(url => fetch(url).catch(err => err))
  3. )

await-to-js - 优雅的try-catch

await-to-js
https://www.npmjs.com/package/await-to-js

  1. import to from 'await-to-js';
  2. // If you use CommonJS (i.e NodeJS environment), it should be:
  3. // const to = require('await-to-js').default;
  4. async function asyncTaskWithCb(cb) {
  5. let err, user, savedTask, notification;
  6. [ err, user ] = await to(UserModel.findById(1));
  7. if(!user) return cb('No user found');
  8. [ err, savedTask ] = await to(TaskModel({userId: user.id, name: 'Demo Task'}));
  9. if(err) return cb('Error occurred while saving task');
  10. if(user.notificationsEnabled) {
  11. [ err ] = await to(NotificationService.sendNotification(user.id, 'Task Created'));
  12. if(err) return cb('Error while sending notification');
  13. }
  14. if(savedTask.assignedUser.id !== user.id) {
  15. [ err, notification ] = await to(NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you'));
  16. if(err) return cb('Error while sending notification');
  17. }
  18. cb(null, savedTask);
  19. }