它不是新的语法功能,而是一种新的写法,允许将回调函数的嵌套,改成链式调用。

为什么需要引入 Promise

异步编程模型

image.png
Web 应用的异步编程模型
上图展示的是一个标准的异步编程模型,页面主线程发起了一个耗时的任务,并将任务交给另外一个进程去处理,这时页面主线程会继续执行消息队列中的任务。等该进程处理完这个任务后,会将该任务添加到渲染进程的消息队列中,并排队等待循环系统的处理。排队结束之后,循环系统会取出消息队列中的任务进行处理,并触发相关的回调操作。

Web 页面的单线程架构决定了异步回调,而异步回调影响到了我们的编码方式,

异步回调

  1. // 执行状态
  2. function onResolve(response){console.log(response) }
  3. function onReject(error){console.log(error) }
  4. let xhr = new XMLHttpRequest()
  5. xhr.ontimeout = function(e) { onReject(e)}
  6. xhr.onerror = function(e) { onReject(e) }
  7. xhr.onreadystatechange = function () { onResolve(xhr.response) }
  8. // 设置请求类型,请求 URL,是否同步信息
  9. let URL = 'https://time.geekbang.com'
  10. xhr.open('Get', URL, true);
  11. // 设置参数
  12. xhr.timeout = 3000 // 设置 xhr 请求的超时时间
  13. xhr.responseType = "text" // 设置响应返回的数据格式
  14. xhr.setRequestHeader("X_TEST","time.geekbang")
  15. // 发出请求
  16. xhr.send();

一个普通的 AJAX 请求有5个回调,这么多的回调会导致代码的逻辑不连贯、不线性,非常不符合人的直觉,这就是异步回调影响到我们的编码方式。

封装异步代码,让处理流程变得线性

image.png
封装请求过程
从图中你可以看到,我们将 XMLHttpRequest 请求过程的代码封装起来了,重点关注输入数据和输出结果。
那我们就按照这个思路来改造代码。首先,我们把输入的 HTTP 请求信息全部保存到一个 request 的结构中,包括请求地址、请求头、请求方式、引用地址、同步请求还是异步请求、安全设置等信息。request 结构如下所示:

  1. //makeRequest 用来构造 request 对象
  2. function makeRequest(request_url) {
  3. let request = {
  4. method: 'Get',
  5. url: request_url,
  6. headers: '',
  7. body: '',
  8. credentials: false,
  9. sync: true,
  10. responseType: 'text',
  11. referrer: ''
  12. }
  13. return request
  14. }
  15. 复制代码

然后就可以封装请求过程了,这里我们将所有的请求细节封装进 XFetch 函数,XFetch 代码如下所示:

  1. //[in] request,请求信息,请求头,延时值,返回类型等
  2. //[out] resolve, 执行成功,回调该函数
  3. //[out] reject 执行失败,回调该函数
  4. function XFetch(request, resolve, reject) {
  5. let xhr = new XMLHttpRequest()
  6. xhr.ontimeout = function (e) { reject(e) }
  7. xhr.onerror = function (e) { reject(e) }
  8. xhr.onreadystatechange = function () {
  9. if (xhr.status = 200)
  10. resolve(xhr.response)
  11. }
  12. xhr.open(request.method, URL, request.sync);
  13. xhr.timeout = request.timeout;
  14. xhr.responseType = request.responseType;
  15. // 补充其他请求信息
  16. //...
  17. xhr.send();
  18. }
  19. 复制代码

这个 XFetch 函数需要一个 request 作为输入,然后还需要两个回调函数 resolve 和 reject,当请求成功时回调 resolve 函数,当请求出现问题时回调 reject 函数。
有了这些后,我们就可以来实现业务代码了,具体的实现方式如下所示:

  1. XFetch(makeRequest('https://time.geekbang.org'),
  2. function resolve(data) {
  3. console.log(data)
  4. }, function reject(e) {
  5. console.log(e)
  6. })

新的问题:回调地狱

上面的示例代码已经比较符合人的线性思维了,在一些简单的场景下运行效果也是非常好的,不过一旦接触到稍微复杂点的项目时,你就会发现,如果嵌套了太多的回调函数就很容易使得自己陷入了回调地狱。

  1. XFetch(makeRequest('https://time.geekbang.org/?category'),
  2. function resolve(response) {
  3. console.log(response)
  4. XFetch(makeRequest('https://time.geekbang.org/column'),
  5. function resolve(response) {
  6. console.log(response)
  7. XFetch(makeRequest('https://time.geekbang.org')
  8. function resolve(response) {
  9. console.log(response)
  10. }, function reject(e) {
  11. console.log(e)
  12. })
  13. }, function reject(e) {
  14. console.log(e)
  15. })
  16. }, function reject(e) {
  17. console.log(e)
  18. })

这段代码之所以看上去很乱,归结其原因有两点:

  • 第一是嵌套调用,下面的任务依赖上个任务的请求结果,并在上个任务的回调函数内部执行新的业务逻辑,这样当嵌套层次多了之后,代码的可读性就变得非常差了。
  • 第二是任务的不确定性,执行每个任务都有两种可能的结果(成功或者失败),所以体现在代码中就需要对每个任务的执行结果做两次判断,这种对每个任务都要进行一次额外的错误处理的方式,明显增加了代码的混乱程度。

原因分析出来后,那么问题的解决思路就很清晰了:

  • 第一是消灭嵌套调用
  • 第二是合并多个任务的错误处理

    使用 promise 重构

    消灭嵌套调用

    Promise 通过回调函数延迟绑定和回调函数返回值穿透的技术,解决了循环嵌套。
    1. function XFetch(request) {
    2. function executor(resolve, reject) {
    3. let xhr = new XMLHttpRequest()
    4. xhr.open('GET', request.url, true)
    5. xhr.ontimeout = function (e) { reject(e) }
    6. xhr.onerror = function (e) { reject(e) }
    7. xhr.onreadystatechange = function () {
    8. if (this.readyState === 4) {
    9. if (this.status === 200) {
    10. resolve(this.responseText, this)
    11. } else {
    12. let error = {
    13. code: this.status,
    14. response: this.response
    15. }
    16. reject(error, this)
    17. }
    18. }
    19. }
    20. xhr.send()
    21. }
    22. return new Promise(executor)
    23. }
    接下来,我们再利用 XFetch 来构造请求流程,代码如下:
    1. var x1 = XFetch(makeRequest('https://time.geekbang.org/?category'))
    2. var x2 = x1.then(value => {
    3. console.log(value)
    4. return XFetch(makeRequest('https://www.geekbang.org/column'))
    5. })
    6. var x3 = x2.then(value => {
    7. console.log(value)
    8. return XFetch(makeRequest('https://time.geekbang.org'))
    9. })
    10. x3.catch(error => {
    11. console.log(error)
    12. })

首先,Promise 实现了回调函数的延时绑定。回调函数的延时绑定在代码上体现就是先创建 Promise 对象 x1,通过 Promise 的构造函数 executor 来执行业务逻辑;创建好 Promise 对象 x1 之后,再使用 x1.then 来设置回调函数。
在执行 executor 里面的 resolve函数的时候回调函数还没有绑定,所以称为延时绑定,所以 resolve 必定是异步的回调

其次,需要将回调函数 onResolve 的返回值穿透到最外层。因为我们会根据 onResolve 函数的传入值来决定创建什么类型的 Promise 任务,创建好的 Promise 对象需要返回到最外层,这样就可以摆脱嵌套循环了。你可以先看下面的代码:
image.png
回调函数返回值穿透到最外层

合并多个任务的错误处理

  1. function executor(resolve, reject) {
  2. let rand = Math.random();
  3. console.log(1)
  4. console.log(rand)
  5. if (rand > 0.5)
  6. resolve()
  7. else
  8. reject()
  9. }
  10. var p0 = new Promise(executor);
  11. var p1 = p0.then((value) => {
  12. console.log("succeed-1")
  13. return new Promise(executor)
  14. })
  15. var p3 = p1.then((value) => {
  16. console.log("succeed-2")
  17. return new Promise(executor)
  18. })
  19. var p4 = p3.then((value) => {
  20. console.log("succeed-3")
  21. return new Promise(executor)
  22. })
  23. p4.catch((error) => {
  24. console.log("error")
  25. })
  26. console.log(2)

这段代码有四个 Promise 对象:p0~p4。无论哪个对象里面抛出异常,都可以通过最后一个对象 p4.catch 来捕获异常,通过这种方式可以将所有 Promise 对象的错误合并到一个函数来处理,这样就解决了每个任务都需要单独处理异常的问题。

为什么 promise 要引入微任务

模拟 promise 内部解构

  1. function Bromise(executor) {
  2. var onResolve_ = null
  3. var onReject_ = null
  4. // 模拟实现 resolve 和 then,暂不支持 rejcet
  5. this.then = function (onResolve, onReject) {
  6. onResolve_ = onResolve
  7. };
  8. function resolve(value) {
  9. //setTimeout(()=>{
  10. onResolve_(value)
  11. // },0)
  12. }
  13. executor(resolve, null);
  14. }
  1. function executor(resolve, reject) {
  2. resolve(100)
  3. }
  4. // 将 Promise 改成我们自己的 Bromsie
  5. let demo = new Bromise(executor)
  6. function onResolve(value){
  7. console.log(value)
  8. }
  9. demo.then(onResolve)
  10. Uncaught TypeError: onResolve_ is not a function
  11. at resolve (<anonymous>:10:13)
  12. at executor (<anonymous>:17:5)
  13. at new Bromise (<anonymous>:13:5)
  14. at <anonymous>:19:12

执行 executor 的时候反向 onResolve 还是 null
修改 onResolve 延时执行

  1. function resolve(value) {
  2. setTimeout(()=>{
  3. onResolve_(value)
  4. },0)
  5. }

不过使用定时器的效率并不是太高,好在我们有微任务,所以 Promise 又把这个定时器改造成了微任务了,这样既可以让 onResolve_ 延时被调用,又提升了代码的执行效率。

promise 缺点

Promise也有一些缺点。

  • 最大问题是代码冗余,原来的任务被 Promise 包装了一下,不管什么操作,一眼看去都是一堆then,原来的语义变得很不清楚,代码不能很好地表示执行流程。
  • 首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  • 其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  • 第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

如果某些事件不断地反复发生,一般来说,使用 Stream 模式是比部署Promise更好的选择。浏览器用 async/await 解决

promise 状态

promise 有四种状态,其中三个核心状态为Pending,Fulfilled以及Rejected,分别表示该Promise挂起,完成以及拒绝,还有一种初始状态,表示还未执行。

promise 特点

Promise对象有以下两个特点。

  • 对象的状态不受外界影响。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected,<br />这时就称为 resolved(已决议)。如果改变已经发生了,resolve 可以进入 fulfilled 或者 rejected 状态

    promise 对象

每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。比如,f1的回调函数f2,可以写成:

  1. function laodScript(src){
  2. // pending,undefined
  3. return new Promise((resolve, reject) => {
  4. let script = document.createElement('script')
  5. script.src = src
  6. script.onload = () => resolve(src)// fulfilled,result
  7. script.onerror = (err) => reject(err)// rejected
  8. document.head.append(script)
  9. })
  10. }
  11. loadScript('./1.js').then(loadScript('./2.js')).then(loadScript('./3.js'))

优点:回调函数变成了链式写法,平行的方式代替一层一层
程序的流程可以看得很清楚,而且有一整套的配套方法,可以实现许多强大的功能。

Promise 原型上 then,catch finally 方法

  1. function timeout(ms) {
  2. return new Promise((resolve, reject) => {
  3. setTimeout(resolve, ms, 'done');
  4. });
  5. }
  6. timeout(100).then((value) => {
  7. console.log(value);
  8. });

resolve函数

resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例

  1. const p1 = new Promise(function (resolve, reject) {
  2. setTimeout(() => reject(new Error('fail')), 3000)
  3. })
  4. const p2 = new Promise(function (resolve, reject) {
  5. setTimeout(() => resolve(p1), 1000)
  6. })
  7. p2
  8. .then(result => console.log(result))
  9. .catch(error => console.log(error))
  10. // Error: fail

这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变。

调用resolvereject并不会终结 Promise 的参数函数的执行。

  1. new Promise((resolve, reject) => {
  2. resolve(1);
  3. console.log(2);
  4. }).then(r => {
  5. console.log(r);
  6. });
  7. // 2
  8. // 1

最好在它们前面加上return语句

  1. new Promise((resolve, reject) => {
  2. return resolve(1);
  3. // 后面的语句不会执行
  4. console.log(2);
  5. })

promise 实例方法

  • resolved状态的 promise 会回调后面的第一个.then
  • reject 状态的 promise会回调后面的第一个.catch
  • 热和一个 rejected 状态且后面没有 catch 的 promise,都会造成浏览器、node 环境的全局错误

执行 then 和 catch 会返回一个新 Promise,该 promise 最终状态根据 then 和 catch 的回调函数的执行结果决定

  • throw,该 promise 是 rejiected
  • return,该 promise 是 resolved 状态
  • return 了一个 promise,该 promise 会和回调函数 return 的 promise 状态保持一致

    Promise.prototype.then

    语法

    promise.then(onFulfilled,onRejected)
    then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)

  • 如果第一个回调函数 return 不是 promise 数据

第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。

  • 如果第一个回调函数 return 是 promise 数据后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。
  • 如果then 里面没有 return,返回新的 promise 实例

    Promise.prototype.catch

    语法

    Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。如果在 then 中指定了 reject 回调函数,catch 不会接收。catch()方法返回的还是一个 Promise 对象。
    如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误。另外,then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获。如果错误被捕获了,不会传到后面去

优点

用 catch比 then 中第二个参数的好处是,可以捕获 then 方法执行中的错误

抛出错误

reject()方法的作用,等同于抛出错误。

  1. // 写法一
  2. const promise = new Promise(function(resolve, reject) {
  3. try {
  4. throw new Error('test');
  5. } catch(e) {
  6. reject(e);
  7. }
  8. });
  9. promise.catch(function(error) {
  10. console.log(error);
  11. });
  12. // 写法二
  13. const promise = new Promise(function(resolve, reject) {
  14. reject(new Error('test'));
  15. });
  16. promise.catch(function(error) {
  17. console.log(error);
  18. });
  19. // 写法三
  20. const promise = new Promise(function(resolve, reject) {
  21. throw new Error('test');
  22. });
  23. promise.catch(function(error) {
  24. console.log(error);
  25. });

如果 Promise 状态已经变成resolved,再抛出错误是无效的。

  1. const promise = new Promise(function(resolve, reject) {
  2. resolve('ok');
  3. throw new Error('test');
  4. });
  5. promise
  6. .then(function(value) { console.log(value) })
  7. .catch(function(error) { console.log(error) });
  8. // ok

上面代码中,Promise 在resolve语句后面,再抛出错误,不会被捕获,等于没有抛出。因为 Promise 的状态一旦改变,就永久保持该状态,不会再变了。

catch 冒泡

Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

  1. getJSON('/post/1.json').then(function(post) {
  2. return getJSON(post.commentURL);
  3. }).then(function(comments) {
  4. // some code
  5. }).catch(function(error) {
  6. // 处理前面三个Promise产生的错误
  7. });

上面代码中,一共有三个 Promise 对象:一个由getJSON()产生,两个由then()产生。它们之中任何一个抛出的错误,都会被最后一个catch()捕获。

和 try/catch 不同点

跟传统的try/catch代码块不同的是,如果没有使用catch()方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即错误会被吞掉。
Promise 内部的错误不会影响到 Promise 外部的代码,promise 外部的脚本继续执行。
如果再 catch 中出错只能依靠下一个 catch 去捕获

Promise.prototype.finally

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。
finally本质上是then方法的特例。

  1. promise.finally(() => {
  2. // 语句
  3. });
  4. // 等同于
  5. promise.then(
  6. result => {
  7. // 语句
  8. return result;
  9. },
  10. error => {
  11. // 语句
  12. throw error;
  13. }
  14. );
  15. Promise.prototype.finally = function (callback) {
  16. let P = this.constructor;
  17. return this.then(
  18. value => P.resolve(callback()).then(() => value),
  19. reason => P.resolve(callback()).then(() => { throw reason; })
  20. );
  21. };
  22. Promise.prototype.done = function (onFulfilled, onRejected) {
  23. this.then(onFulfilled, onRejected)
  24. .catch(function (reason) {
  25. // 抛出一个全局错误
  26. setTimeout(() => { throw reason; }, 0);
  27. });
  28. };
  1. Promise.resolve(2).then((data) => { return data })
  2. .then(data => {
  3. console.log(data)
  4. });
  5. // 2
  6. Promise.resolve(2).finally(() => {})
  7. .then(data => {
  8. console.log(data)
  9. });
  10. // 2

串行-链式

  1. // 4是没有,1,2,3都有
  2. loadScript('./4.js')
  3. .then(() => {
  4. loadScript('./2.js')
  5. }, (err) => {
  6. console.log(err)
  7. })
  8. .then(() => {
  9. loadScript('./3.js')
  10. }, (err) => {
  11. console.log(err)
  12. })
  13. // 3
  14. loadScript('./1.js')
  15. .then(() => {
  16. loadScript('./2.js')
  17. }, (err) => {
  18. console.log(err)
  19. })
  20. .then(() => {
  21. loadScript('./3.js')
  22. }, (err) => {
  23. console.log(err)
  24. })
  25. // 1,2,3
  26. loadScript('./1.js')
  27. .then(() => {
  28. loadScript('./4.js')
  29. }, (err) => {
  30. console.log(err)
  31. })
  32. .then(() => {
  33. loadScript('./3.js')
  34. }, (err) => {
  35. console.log(err)
  36. })
  37. // 1 3
  38. loadScript('./1.js')
  39. .then(() => {
  40. return loadScript('./4.js')
  41. }, (err) => {
  42. console.log(err)
  43. })
  44. .then(() => {
  45. loadScript('./3.js')
  46. }, (err) => {
  47. console.log(err)
  48. })
  49. // 1

promise 静态方法

  1. function test (bool) {
  2. if (bool) {
  3. return new Promise((resolve, reject) => { resolve(30)
  4. })
  5. } else {
  6. // 42 数字不行要 promise
  7. return Promise.resolve(42)
  8. // return Promise.reject(new Error('ss'))
  9. }
  10. }
  11. test.then()

多个错误一起捕获

  1. loadScript('./1.js')
  2. .then(() => {
  3. return loadScript('./4.js')
  4. })
  5. .then(() => {
  6. loadScript('./3.js')
  7. }).catch((err) => {
  8. console.log(err)
  9. })

用 reject 去捕获,不要用 throw new Error

并行 Promise.all()

从Promise.all([ .. ])返回的主promise在且仅在所有的成员promise都完成后才会完 成。如果这些promise中有任何一个被拒绝的话,主Promise.all([ .. ])promise就会立 即被拒绝,并丢弃来自其他所有 promise 的全部结果。

  1. const p1 = Promise.resolve(1)
  2. const p2 = Promise.resolve(2)
  3. const p3 = Promise.resolve(3)
  4. Promise.all([p1,p2,p3]).then(value => {
  5. console.log(value)
  6. })
  7. // [1, 2, 3]

注意,如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()catch方法。

  1. const p1 = new Promise((resolve, reject) => {
  2. resolve('hello');
  3. })
  4. .then(result => result)
  5. .catch(e => e);
  6. const p2 = new Promise((resolve, reject) => {
  7. throw new Error('报错了');
  8. })
  9. .then(result => result)
  10. .catch(e => e);
  11. Promise.all([p1, p2])
  12. .then(result => console.log(result))
  13. .catch(e => console.log(e));
  14. // 会进 then 方法
  15. // ["hello", Error: 报错了]

如果p2没有自己的catch方法,就会调用Promise.all()catch方法。

竞争 Promise.race()

与Promise.all([ .. ])类似,一旦有任何一个Promise决议为完成,Promise.race([ .. ]) 就会完成;一旦有任何一个 Promise 决议为拒绝,它就会拒绝。
可以用来防止回调未被调用
场景:主 cdn,备用 cdn,有一个加载了就行
并行,串行都不太符合

  1. const p1 = () => {
  2. return new Promise((resolve, reject) => {
  3. setTimeout(() => {
  4. resolve(1)
  5. }, 1000)
  6. })
  7. }
  8. const p2 = () => {
  9. return new Promise((resolve, reject) => {
  10. resolve(2)
  11. }).then(() => {return 2})
  12. }
  13. // question: 都会请求?
  14. Promise.race([p1(),p2()]).then((value) => {
  15. console.log(value) // 2
  16. })

下面是一个例子,如果指定时间内没有获得结果,就将 Promise 的状态变为reject,否则变为resolve

  1. const p = Promise.race([
  2. fetch('/resource-that-may-take-a-while'),
  3. new Promise(function (resolve, reject) {
  4. setTimeout(() => reject(new Error('request timeout')), 5000)
  5. })
  6. ]);
  7. p
  8. .then(console.log)
  9. .catch(console.error);

Promise.allSettled()

Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。该方法由 ES2020 引入。注意和 all 的区别,

  1. const promises = [
  2. fetch('/api-1'),
  3. fetch('/api-2'),
  4. fetch('/api-3'),
  5. ];
  6. await Promise.allSettled(promises);
  7. removeLoadingIndicator();

该方法返回的新的 Promise 实例,一旦结束,状态总是fulfilled,不会变成rejected。状态变成fulfilled后,Promise 的监听函数接收到的参数是一个数组,每个成员对应一个传入Promise.allSettled()的 Promise 实例。

  1. const resolved = Promise.resolve(42);
  2. const rejected = Promise.reject(-1);
  3. const allSettledPromise = Promise.allSettled([resolved, rejected]);
  4. allSettledPromise.then(function (results) {
  5. console.log(results);
  6. });
  7. // [
  8. // { status: 'fulfilled', value: 42 },
  9. // { status: 'rejected', reason: -1 }
  10. // ]

有时候,我们不关心异步操作的结果,只关心这些操作有没有结束。这时,Promise.allSettled()方法就很有用。如果没有这个方法,想要确保所有操作都结束,就很麻烦。Promise.all()方法无法做到这一点。

  1. const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
  2. const results = await Promise.allSettled(promises);
  3. // 过滤出成功的请求
  4. const successfulPromises = results.filter(p => p.status === 'fulfilled');
  5. // 过滤出失败的请求,并输出原因
  6. const errors = results
  7. .filter(p => p.status === 'rejected')
  8. .map(p => p.reason);

Promise.resolve()

有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用。
等价于

  1. Promise.resolve('foo')
  2. // 等价于
  3. new Promise(resolve => resolve('foo'))
  • 参数是一个 Promise 实例如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。
  • 参数是一个thenable对象
    1. const jsPromise = Promise.resolve($.ajax('/whatever.json'));
    上面代码将 jQuery 生成的deferred对象,转为一个新的 Promise 对象。
  1. let thenable = {
  2. then: function(resolve, reject) {
  3. resolve(42);
  4. }
  5. };
  6. let p1 = Promise.resolve(thenable);
  7. p1.then(function(value) {
  8. console.log(value); // 42
  9. });
  • 参数不是具有then方法的对象,或根本就不是对象Promise.resolve方法返回一个新的 Promise 对象,状态为resolved
  • 不带有任何参数Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。需要注意的是,立即resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。 ```javascript setTimeout(function () { console.log(‘three’); }, 0);

Promise.resolve().then(function () { console.log(‘two’); });

console.log(‘one’);

// one // two // three

  1. <a name="R9ZzW"></a>
  2. ## Promise.reject()
  3. `Promise.reject(reason)`方法也会返回一个新的 Promise 实例,该实例的状态为`rejected`。
  4. ```javascript
  5. const p = Promise.reject('出错了');
  6. // 等同于
  7. const p = new Promise((resolve, reject) => reject('出错了'))

注意,Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致。

  1. const thenable = {
  2. then(resolve, reject) {
  3. reject('出错了');
  4. }
  5. };
  6. Promise.reject(thenable)
  7. .catch(e => {
  8. console.log(e === thenable)
  9. })
  10. // true

Promise.try() stage-1

同步任务变成异步任务执行

  1. const f = () => console.log('now');
  2. Promise.resolve().then(f);
  3. console.log('next');
  4. // next
  5. // now

让同步函数同步执行,异步函数异步执行。

  • async ```javascript // 同步 const f = () => console.log(‘now’); (async () => f())(); console.log(‘next’); // now // next

// 异步 const f = () => { setTimeout(() => { console.log(‘async’) }) } (async () => f())().then((data) => { console.log(data)}).catch(_ => {}); console.log(‘next’); // next async

  1. - promise.try
  2. ```javascript
  3. const f = () => console.log('now');
  4. Promise.try(f);
  5. console.log('next');