Table of Contents generated with DocToc (opens new window)

Promise对象:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

# Promise的状态

三种状态:

  • pending(进行中)
  • fulfilled(已成功)
  • rejected(已失败)

只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。

// Promise实例
const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
1
2
3
4
5
6
7
8
9
10

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。

resolve和reject是两个回调函数

# then-catch

then方法 (opens new window)

then() 方法返回一个 Promise (opens new window)。它最多需要有两个参数:Promise 的成功和失败情况的回调函数。

then也可以捕获错误

p.then(onFulfilled[, onRejected]);
/* 
onFulfilled 可选
当 Promise 变成接受状态(fulfilled)时调用的函数。该函数有一个参数,即接受的最终结果(the fulfillment  value)。如果该参数不是函数,则会在内部被替换为 (x) => x,即原样返回 promise 最终结果的函数
onRejected 可选
当 Promise 变成拒绝状态(rejected)时调用的函数。该函数有一个参数,即拒绝的原因(rejection reason)。  如果该参数不是函数,则会在内部被替换为一个 "Thrower" 函数 (it throws an error it received as argument)。
*/
1
2
3
4
5
6
7
const pm1 = new Promise((resolve, reject) => {
  resolve('success')
})

pm1.then(
  (val) => {
    console.log(val)
  },
  (err) => {
    console.log(err)
  }
)
1
2
3
4
5
6
7
8
9
10
11
12

catch方法 (opens new window)

p.catch(onRejected);

p.catch(function(reason) {
   // 拒绝
});
1
2
3
4
5

catch实际上是then的语法糖,mdn关于catch的解释

catch() 方法返回一个Promise (opens new window),并且处理拒绝的情况。它的行为与调用Promise.prototype.then(undefined, onRejected) (opens new window) 相同。 (事实上, calling obj.catch(onRejected) 内部calls obj.then(undefined, onRejected)).

# then vs then catch

myPromise.then(function() {
   // Some error may happen
   throw('An exception that would be caught');
}).catch(function() {
    console.log('error');
});
// Is the same as this, the errHandle tries to catch any unhandled error
// from previous result.
myPromise.then(func, null).then(null, errHandle);


myPromise.then(function() {
   // Some error may happen
   throw('An unhandled exception.');
}, function() {
    // This won't log the error if it happens in the 
    // some error may happen block.
    console.log('error');
});
// Is the same as this, the errHandle will handle errors from previous result,
// but it won't handle errs in func.
myPromise.then(func, errHandle)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

**一般总是建议,Promise 对象后面要跟catch()方法,这样可以处理 Promise 内部发生的错误。**catch()方法返回的还是一个 Promise 对象,因此后面还可以接着调用then()方法。

const someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    // 下面一行会报错,因为x没有声明
    resolve(x + 2);
  });
};

someAsyncThing()
.catch(function(error) {
  console.log('oh no', error);
})
.then(function() {
  console.log('carry on');
});
// oh no [ReferenceError: x is not defined]
// carry on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

上面代码运行完catch()方法指定的回调函数,会接着运行后面那个then()方法指定的回调函数。如果没有报错,则会跳过catch()方法。

# 使用Promise进行错误处理

使用 promise 进行错误处理 (opens new window)

new Promise((resolve, reject) => {
  throw new Error("Whoops!");
}).catch(alert); // Error: Whoops!
1
2
3

……与下面这段代码工作上完全相同:

new Promise((resolve, reject) => {
  reject(new Error("Whoops!"));
}).catch(alert); // Error: Whoops!
1
2
3

# Promise的学习

var p = new Promise(function (resolve, reject) {
  //做一些异步操作
  setTimeout(function () {
    console.log('执行完成')
    resolve('随便什么数据')
  }, 2000)
})

p.then((res) => console.log(res)).catch((err) => console.log(err))
// then方法接收一个参数,是函数,并且会拿到我们在p中调用resolve时传的参数。
// 运行这段代码,会在2秒后输出"执行完成",紧接着输出"随便什么数据"。
1
2
3
4
5
6
7
8
9
10
11

# 链式操作+递归

function runAsync(param) {
  return new Promise(function (resolve, reject) {
    //做一些异步操作
    setTimeout(function () {
      console.log('执行完成')
      resolve(param)
    }, 500)
  })
}

runAsync(1)
  .then((res) => {
    console.log(1)
    return runAsync(2)
  })
  .then((res) => {
    console.log(res)
    return runAsync(3)
  })
  .then((res) => console.log(res))
/* 执行完成
	 1
	 执行完成
   2
   执行完成
   3  */
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

# Promise的Api

# Promise.all

等待所有 Promise 完成,并返回所有结果。如果有一个 Promise 失败,则返回失败的结果。

Promise.allSettled:等待所有 Promise 完成,无论成功还是失败,返回每个 Promise 的结果。

Promise.race:等待第一个完成的 Promise 并返回其结果(无论成功还是失败)。

Promise.any:返回第一个成功的Promise结果,如果全部失败,则返回AggregateError对象。

  • 功能: Promise.all 用于接收一个包含多个 Promise 的可迭代对象,并在所有 Promise 都成功解决时才会解决,如果其中任何一个 Promise 被拒绝(rejected),则返回的 Promise 会立即被拒绝,并以第一个被拒绝的 Promise 的错误作为拒绝原因。
  • 行为: 返回的 Promise 解析为一个包含所有 Promise 解析值的数组,该数组的顺序与传入的 Promise 的顺序相同。
  • 适用场景: 当需要同时等待多个异步操作都成功完成后再执行下一步操作时,通常会使用 Promise.all。
function makePromise(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(time);
    }, time);
  });
}
Promise.all([makePromise(2000), makePromise(3000), makePromise(4000)]).then(
  (res) => {
    console.log(res);
  }
);
//[ 2000, 3000, 4000 ]
//exited with code=0 in 5.054 seconds
// 在所有Promise的状态都凝固后再执行then
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Promise.allSettled

  • 功能: Promise.allSettled 用于接收一个包含多个 Promise 的可迭代对象,并在所有 Promise 都被解决后返回一个 Promise,该 Promise 总是解决,不管每个 Promise 的状态是解决还是被拒绝。
  • 行为: 返回的 Promise 解析为一个包含所有 Promise 解决状态的数组,每个数组元素都包含一个对象,该对象表示对应的 Promise 的状态和结果(如果有)。
  • 适用场景: 当需要等待多个异步操作都完成后,无论成功或失败,都执行下一步操作时,通常会使用 Promise.allSettled。
Promise.allSettled([pm2, pm1, pm2, makePromise(5000)])
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
  });
/*
[
  { status: 'fulfilled', value: 'error' },
  { status: 'fulfilled', value: 2000 },
  { status: 'fulfilled', value: 'error' },
  { status: 'fulfilled', value: 5000 }
]
*/
// exited with code=0 in 5.054 seconds

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

当您有多个彼此不依赖的异步任务成功完成时,或者您总是想知道每个promise的结果时,通常使用它。

相比之下,Promise.all() 更适合彼此相互依赖或者在其中任何一个reject时立即结束。

# Promise 防止阻塞

promise防止阻塞

function timeoutPromise(interval) {
  return new Promise((resolve, reject) => {
    setTimeout(function () {
      console.log('done')
      resolve('done')
    }, interval)
  })
}

function timeTest() {
  // return timeoutPromise(2000)
  //   .then(() => timeoutPromise(3000))
  //   .then(() => timeoutPromise(4000))

  // 防止阻塞 赋值的本质 是因为Promise实例一旦建立就会立即执行,而后面的then是为了获取Promise实例的状态
  const timeout1 = timeoutPromise(2000)
  const timeout2 = timeoutPromise(3000)
  const timeout3 = timeoutPromise(4000)
  return timeout1.then(() => timeout2).then(() => timeout3)
}

let startTime = Date.now()
timeTest().then(() => {
  let finishTime = Date.now()
  let timeTaken = finishTime - startTime
  console.log('Time taken in milliseconds: ' + timeTaken)
})
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

对应的async await版本

# Promise防止阻塞

1.Promise存储到变量中可以防止阻塞

const ajax = (time: number) =>
  new Promise((resolve) => {
    setTimeout(() => {
      console.log(time);
      resolve(time);
    }, time);
  });

const start2 = async () => {
    console.time("test");
    const timeout1 = ajax(2000);
    const timeout2 = ajax(3000);
    const timeout3 = ajax(4000);
    await timeout1;
    await timeout2;
    await timeout3;
    console.timeEnd("test");
  };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

赋值的本质 是因为Promise实例一旦建立就会立即执行,而后面的then是为了获取Promise实例的状态

2.Promise.all

const start = async () => {
    console.time("test");
    await Promise.all([ajax(2000), ajax(3000), ajax(4000)]);
    console.timeEnd("test");
  };
1
2
3
4
5

# Promise的执行顺序

Mdn:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises

# 时序

为了避免意外,即使是一个已经变成 resolve 状态的 Promise,传递给 then() 的函数也总是会被异步调用:

Promise.resolve().then(() => console.log(2));
console.log(1); // 1, 2
1
2

传递到 then() 中的函数被置入到一个微任务队列中,而不是立即执行,这意味着它是在 JavaScript 事件队列的所有运行时结束了,且事件队列被清空之后,才开始执行:

const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

wait().then(() => console.log(4));
Promise.resolve().then(() => console.log(2)).then(() => console.log(3));
console.log(1); // 1, 2, 3, 4
1
2
3
4
5

# Async await reject问题

async await语法不能接收reject,需要使用try catch处理

# Promise并行

先来看几段代码

代码一:

const getLoader = async () => {
    const numberVal = await route.meta.number
    const nameVal = await route.meta.name
    number.value = numberVal
    name.value = nameVal
}
1
2
3
4
5
6
  1. 串行执行:两个 await 是依次执行的,先等待一个 Promise,再等待另一个。

代码二:

const getLoader = async () => {
    const numberVal = route.meta.number
    const nameVal = route.meta.name
    number.value = await numberVal
    name.value = await nameVal
}
1
2
3
4
5
6

两个 Promise 可以并行执行