从实际应用看promise系列方法,链式调用等【简单易懂!】

129 阅读8分钟
  • promise实例方法(定义在构造函数的原型对象上的方法)
    • 1️⃣Promise.prototype.then(onFulfilled, onRejected)
    • 2️⃣Promise.prototype.catch(onRejected)
    • 3️⃣Promise.prototype.finally(onFinally)
  • promise静态方法(直接定义在构造函数(类)上的方法)
    • 4️⃣Promise.resolve
    • 5️⃣Promise.reject
    • 6️⃣Promise.all
    • 7️⃣Promise.race
    • 8️⃣Promise.allSettled
    • ⑨Promise.any()

一、实际应用

1.1 !!创建Promise实例!!

  • 必须传入一个执行器函数作为参数: new Promise(); // 报错
    • 函数接收两个参数:resolve,reject
    • resolve(异步返回的结果):将Promise对象的状态从pending变为resolved;reject():将Promise对象的状态从pending变为rejected
    • 只有当Promise的状态变为resolved或者rejected时,才会执行实例方法or静态方法
    • promise内部发生错误,错误会被reject

对于概念的应用以及解释

// 传入执行器函数创建Promise实例
const myPromise = new Promise((reoslve, reject) => {
    setTimeout(() => {
        const success = true;
        if(success){
            resolve()
        }else {
            reject()
        }
    }, 3000)
})

// `resolve(异步返回的结果)`:将Promise对象的状态从pending变为resolved;`reject()`:将Promise对象的状态从pending变为rejected
const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
})
console.log('1', promise1);
// ------'promise1'   '1' Promise{<pending>}

//  只有当Promise的状态变为`resolved或者rejected`时,才会执行实例方法or静态方法
const promise = new Promise((resolve, reject) => {
  console.log(1);
  console.log(2);
});
promise.then(() => {
  console.log(3);
});
console.log(4);
//。------ 结果:1 2 4

// promise内部发生错误,错误会被reject
const promise = new Promise(resolve => {
  throw new Error("aa") // 等同于reject(new Error("aa"))
  resolve("resolve");
});
console.log(promise) // Promise {<rejected>: Error: aa ...... }

1.2 1️⃣Promise.prototype.then(onFulfilled, onRejected)的使用

  • Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
  • Promise.prototype.then返回的是另外一个Promise实例
// 应用
promise.then(
  value => {
    // resolved时调用,value为resolve函数返回的参数
    console.log(value);
  },
  err => {
    // rejected时调用,err为reject函数返回的参数
    console.log(err);
  }
);

// 返回的是另外一个Promise的实例
const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
  resolve('resolve1')
})
const promise2 = promise1.then(res => {
  console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);
// 结果: 'promise1' '1' Promise{<resolved>: 'resolve1'} '2' Promise{<pending>} 'resolve1'

1.3 2️⃣Promise.prototype.catch(onRejected) 的使用

  1. catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。catch()方法也会返回一个 Promise 对象(同then方法)

    • 如果 Promise 状态已经变成resolved,再抛出错误是无效的。
    • promise中所有没有被处理的错误都会冒泡到最后一个catch中
  2. 实现“失败继续请求,请求n次不在继续请求”

// 模拟请求函数
const mockRequest = (): Promise<string> => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const isSuccess = Math.random() < 0.3;
      if (isSuccess) {
        resolve('成功');
      } else {
        reject('失败');
      }
    }, 1000);
  });
};

// 失败重复触发请求函数
const retryRequest = (maxRetries: number): Promise<string> => {
  let attempt = 0;

  const attemptRequest = (): Promise<string> => {
    return new Promise((resolve, reject) => {
      mockRequest()
        .then(resolve)
        .catch((error) => {
          attempt++;
          if (attempt >= maxRetries) {
            reject(`请求失败 ${maxRetries} 次`);
          } else {
            console.log(`请求失败,重试第 ${attempt} 次`);
            attemptRequest().then(resolve).catch(reject);
          }
        });
    });
  }
  return attemptRequest();
};

retryRequest(3)
  .then((res) => {
    console.log('3 次之内请求成功:', res);
  })
  .catch((err) => {
    console.log('3 次之内请求失败:', err);
  });

1.4 4️⃣Promise.resolve 的使用5️⃣Promise.reject 的使用

Promise.resolve()返回一个以给定值解析后的Promise 对象。Promise.reject()方法会返回一个新的状态为rejected的Promise 实例

一、 Promise.resolve

参数分为一下四种情况

  • promise实例
  • thenable对象
  • 非对象
  • 参数为空
  1. 参数是promise实例,那么 Promise.resolve 将不做任何修改,原封不动地返回这个实例。
const promise = new Promise((resolve, reject) => {
  reject("resolve");
});
let p = Promise.resolve(promise);
console.log(p);   // Promise { <rejected> "resolve" }
console.log(promise);   // Promise { <rejected> "resolve" }
  1. 参数是thenable对象:具有 then 方法的对象。
    • Promise.resolve() 方法会将这个对象转为 Promise 对象,然后立即执行 thenable 对象的 then() 方法
    • 返回的 Promise 会“跟随”这个 thenable 对象,采用它的最终状态。
// thenable对象
let thenable = {
 then(resolve, reject) {
   reject(42);
 }
};

let p1 = Promise.resolve(thenable);
p1.then(value => {
 console.log("resolved", value);
}, err => {
 console.log("rejected", err); // 42
});

  1. 非对象: Promise.resolve() 方法会返回一个状态为 resolved 的 Promise 对象,其 resolved 值即为传入的参数。
const p = Promise.resolve('Hello');
p.then(s => {
  console.log(s); // Hello
});
  1. 参数为空: Promise.resolve() 方法允许调用时不带参数,直接返回一个 resolved 状态的 Promise 对象。
Promise.resolve();
// 相当于
new Promise(resolve => resolve(undefined));
二、 Promise.reject

Promise.reject() 方法返回一个新的状态为 rejected 的 Promise 实例,即使是一个 resolved 状态的 Promise 也不行。

let promise = Promise.resolve("foo");
let p1 = Promise.reject(promise);
p1.then(value => {
  console.log("resolved", value);
}, err => {
  console.log("rejected", err); // Promise {<fulfilled>: 'foo'}
});
三、 关于链式调用(实例方法then;静态方法:reject,resolve的结合应用如下:【更好理解Promise的行为和工作机制】
  1. 未传入处理程序:Promise.resolve("foo") 创建了一个立即解决的 Promise
const p1 = Promise.resolve("foo");
let pt1 = p1.then();
console.log(p1); // Promise { <resolved>: "foo" }
console.log(pt1); // Promise { <resolved>: "foo" }

  • p1.then() 调用了 then 方法,但没有传入处理程序(即 onFulfilled 和 onRejected 都是 undefined)。
  • 如果 then 方法没有传入处理程序,它会返回一个新的 Promise,该 Promise 会继承原始 Promise 的状态和值。因此,pt1 将会解决为 "foo",与 p1 的值相同。
  1. 未成功调用then方法
const p2 = Promise.reject("bar");
let pt2 = p2.then(value => {
  console.log(value);
});
console.log(p2); // Promise { <rejected>: "bar" }
console.log(pt2); // Promise { <rejected>: "bar" }
  • Promise.reject("bar") 创建了一个立即拒绝的 Promise,其原因是 "bar"
  • p2.then(value => { console.log(value); }) 调用了 then 方法,并传入了一个 onFulfilled 处理程序。
  • 由于 p2 已经被拒绝,onFulfilled 不会被调用,因此 console.log(value) 不会执行。
  • pt2 是 p2.then() 返回的新 Promise。因为 p2 被拒绝且 then 方法没有传入 onRejected 处理程序,pt2 也会被拒绝,其原因与 p2 相同,即 "bar"
  1. 返回的是另外一个 Promise 的实例
const promise1 = new Promise((resolve, reject) => {
  console.log('promise1');
  resolve('resolve1');
});
const promise2 = promise1.then(res => {
  console.log(res);
});
console.log('1', promise1); // Promise { <resolved>: "resolve1" }
console.log('2', promise2); // Promise { <pending> }

// 输出顺序:
// 'promise1'
// '1' Promise { <resolved>: "resolve1" }
// '2' Promise { <pending> }
// 'resolve1'

  • new Promise((resolve, reject) => { ... }) 创建了一个新的 Promise 对象。在执行器函数中,console.log('promise1') 会立即执行,并且 resolve('resolve1') 会将 promise1 的状态设置为已解决,值为 'resolve1'
  • promise1.then(res => { console.log(res); }) 返回了一个新的 Promise 对象 promise2then 方法中的回调函数会在 promise1 解决后执行。
  • console.log('1', promise1) 会输出 Promise { <resolved>: "resolve1" },因为 promise1 已经解决。
  • console.log('2', promise2) 会输出 Promise { <pending> },因为 promise2 是由 then 方法返回的新 Promise,此时它的状态是 pending,直到 then 方法中的回调函数执行完毕。
  • 当 promise1 解决后,then 方法中的回调函数 res => { console.log(res); } 会被调用,输出 'resolve1'。此时,promise2 的状态将从 pending 变为 resolved,其值为 undefined,因为 then 方法中的回调函数没有返回值。

1.5 ⑥promise静态方法all的使用

  1. Promise.all 接受一个包含多个 Promise 实例的可迭代对象(通常是数组),并返回一个新的 Promise 实例。这些 Promise 实例会并行执行。Promise.all 返回的 Promise 会在以下两种情况下被处理:

    • 全部成功:如果所有 Promise 都成功,则返回一个包含所有成功结果的数组。
    • 任一失败:如果有任意一个 Promise 失败,则返回第一个失败的 Promise 的错误。
  2. Promise.all: 用于需要所有异步操作都成功后才进行下一步操作的场景,并行获取多个API数据并在所有数据都获取成功后再处理

const promise1 = fetch('https://api.example.com/data1');
const promise2 = fetch('https://api.example.com/data2');
const promise3 = fetch('https://api.example.com/data3');

Promise.all([promise1, promise2, promise3])
  .then((responses) => {
    return Promise.all(responses.map(response => response.json()));
  })
  .then((data) => {
    console.log(data); // 所有数据都成功获取
  })
  .catch((error) => {
    console.error('有一个请求失败:', error);
  });

1.6 7️⃣promise静态方法race的使用

页面分享是异步操作么?

是的!why?用户点击分享之后即使没有立马响应也不会阻塞其他操作。

// 分享函数的定义
export async function shareAsync(
    url: string,
    title: string
): Promise<boolean>{
    // 超时机制,3s内没有执行分享操作,定时器Promise将会首先完成
    // promise静态方法race
    const reuslt = await Promise.race({
        shareActivityAsync({   // 异步请求分享给微信好友
            url,
            title
        }),
        // promise实例的创建
        new Promise(resolve => setTimeout(() => resolve(true), 3000))
    })
    return result as boolean;
}
// 调用分享函数
const success = await shareAsync(
    url,
    title
)
if(success){
    toast('分享成功')
} else {
    toast('分享失败请重试')
}

1.7 ⑧promise静态方法allSettled

Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束,参数与Promise.all()方法一样

let p2 = Promise.reject(2);
let promise = Promise.allSettled([1, p2, 3]);
promise.then(value => {
  console.log(value); // [{status: "fulfilled", value: 1},{status: "rejected", reason: 2},{status: "fulfilled", value: 3}]
});
console.log(promise);
  • Promise.allSettled()的返回的promise实例状态只可能变成resolved,它的监听函数收到的参数是一个数组,该数组的每个成员都是一个对象,每一个对象都有status属性,该属性的值只可能是字符fulfilled或字符串rejected
  • fulfilled时,对象有value属性,rejected时有reason属性,对应两种状态的返回值。

1.8 ⑨Promise.any()

该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。
Promise.any()Promise.race()方法很像,只有一点不同,就是不会因为某个 Promise 变成rejected状态而结束。

let p1 = Promise.reject(1);
let p2 = Promise.reject(2);
let promise = Promise.any([p1, p2, 3]);
promise.then(value => {
  console.log(value); // 3
});
console.log(promise);

当所有的实例返回的状态都是rejected时,Promise.any()会返回一个的实例状态才为rejected

let p1 = Promise.reject(1);
let p2 = Promise.reject(2);
let promise = Promise.any([p1, p2]);
promise
  .then(value => {
  console.log(value);
})
  .catch(value => {
  console.log(value); // AggregateError: All promises were rejected
});
console.log(promise);

1.9 ⑩Promise.try()

实际开发中,经常遇到一种情况:不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误。一般就会采用下面的写法。

const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// next
// now

上面的写法有一个缺点,就是如果f是同步函数,那么它会在本轮事件循环的末尾执行。
鉴于这是一个很常见的需求,所以现在有一个提案,提供Promise.try方法替代上面的写法。

const f = () => console.log('now');
Promise.try(f);
console.log('next');
// now
// next

部分参考于: