关于 Promise 的 9 个提示

更新日期: 2019-02-02阅读: 2.1k标签: Promise

1. 你可以在 .then 里面 return 一个 Promise

让我来说明这最重要的一点

是的!你可以在 .then 里面 return 一个 Promise

而且,return 的这个 Promise 将在下一个 .then 中自动解析。

.then(r => {
    return serverStatusPromise(r); // 返回 { statusCode: 200 } 的 Promise
})
.then(resp => {
    console.log(resp.statusCode); // 200; 注意自动解析的 promise
})


2. 每次执行 .then 的时候都会自动创建一个新的 Promise

如果熟悉 javascript 的链式风格,那么你应该会感到很熟悉。但是对于一个初学者来说,可能就不会了。

在 Promise 中不论你使用 .then 或者 .catch 都会创建一个新的 Promise。这个 Promise 是刚刚链式调用的 Promise 和 刚刚加上的 .then / .catch 的组合。

让我们来看一个:

var statusProm = fetchServerStatus();

var promA = statusProm.then(r => (r.statusCode === 200 ? "good" : "bad"));

var promB = promA.then(r => (r === "good" ? "ALL OK" : "NOTOK"));

var promC = statusProm.then(r => fetchThisAnotherThing());

上面 Promise 的关系可以在流程图中清晰的描述出来:


需要特别注意的是 promA、 promB 和 promC 全部都是不同的但是有关联的 Promise。

我喜欢把 .then 想像成一个大型管道,当上游节点出现问题时,水就会停止流向下游。例如,如果 promB 失败,下游节点不会受到影响,但是如果 statusProm 失败,那么下游的所有节点都将受到影响,即 rejected。


3. 对调用者来说,Promise 的 resolved/rejected 状态是唯一的

我认为这个是让 Promise 好好运行的最重要的事情之一。简单来说,如果在你的应用中 Promise 在很多不同的模块之间共享,那么当 Promise 返回 resolved/rejected 状态时,所有的调用者都会收到通知。

这也意味着没有人可以改变你的 Promise,所以可以放心的把它传递出去。
function yourFunc() {
  const yourAwesomeProm = makeMeProm();

  yourEvilUncle(yourAwesomeProm); // 无论 Promise 受到了怎样的影响,它最终都会成功执行

  return yourAwesomeProm.then(r => importantProcessing(r));
}

function yourEvilUncle(prom) {
  return prom.then(r => Promise.reject("destroy!!")); // 可能遭受的影响
}

通过上面的例子可以看出,Promise 的设计使得自身很难被改变。正如我上面所说的:"保持冷静,并将 Promise 传递下去"。


4. Promise 构造函数不是解决方案

我看到很多开发者喜欢用构造函数的风格,他们认为这就是 Promise 的方式。但这却是一个谎言,实际的原因是构造函数 api 和之前回调函数的 API 相似,而且这样的习惯很难改变。

如果你发现自己正在到处使用 Promise 构造函数,那你的做法是错的!

要真正的向前迈进一步并且摆脱回调,你需要小心谨慎并且最小程度地使用 Promise 构造函数。

让我们看一下使用 Promise 构造函数 的具体情况:

return new Promise((res, rej) => {
  fs.readFile("/etc/passwd", function(err, data) {
    if (err) return rej(err);
    return res(data);
  });
});

Promise 构造函数 应该只在你想要把回调转换成 Promise 时使用
一旦你掌握了这种创建 Promise 的优雅方式,它将会变的非常有吸引力。

让我们看一下冗余的 Promise 构造函数。

错误的

return new Promise((res, rej) => {
    var fetchPromise = fetchSomeData(.....);
    fetchPromise
        .then(data => {
            res(data); // 错误!!!
        })
        .catch(err => rej(err))
})

正确的

return fetchSomeData(...); // 正确的!

用 Promise 构造函数 封装 Promise 是多余的,并且违背了 Promise 本身的目的

高级技巧

如果你是一个 nodejs 开发者,我建议你可以看一看 util.promisify。这个方法可以帮助你把 node 风格的回调转换为 Promise。

const {promisify} = require('util');
const fs = require('fs');

const readFileAsync = promisify(fs.readFile);

readFileAsync('myfile.txt', 'utf-8')
  .then(r => console.log(r))
  .catch(e => console.error(e));

</div>


5. 使用 Promise.resolve

Javascript 提供了 Promise.resolve 方法,像下面的例子这样简洁:

var similarProm = new Promise(res => res(5));
// ^^ 等价于
var prom = Promise.resolve(5);

它有多种使用情况,我最喜欢的一种是可以把普通的(异步的)js 对象转化成 Promise。

// 将同步函数转换为异步函数
function foo() {
  return Promise.resolve(5);
}

当不确定它是一个 Promise 还是一个普通的值的时候,你也可以做一个安全的封装。

function goodProm(maybePromise) {
  return Promise.resolve(maybePromise);
}

goodProm(5).then(console.log); // 5

var sixPromise = fetchMeNumber(6);

goodProm(sixPromise).then(console.log); // 6

goodProm(Promise.resolve(Promise.resolve(5))).then(console.log); // 5, 注意,它会自动解析所有的 Promise!


6.使用 Promise.reject

Javascript 也提供了 Promise.reject 方法。像下面的例子这样简洁:

var rejProm = new Promise((res, reject) => reject(5));

rejProm.catch(e => console.log(e)) // 5

我最喜欢的用法是提前使用 Promise.reject 来拒绝。

function foo(myVal) {
    if (!mVal) {
        return Promise.reject(new Error('myVal is required'))
    }
    return new Promise((res, rej) => {
        // 从你的大回调到 Promise 的转换!
    })
}

简单来说,使用 Promise.reject 可以拒绝任何你想要拒绝的 Promise。

在下面的例子中,我在 .then 里面使用:

.then(val => {
  if (val != 5) {
    return Promise.reject('Not Good');
  }
})
.catch(e => console.log(e)) // 这样是不好的

注意:你可以像 Promise.resolve 一样在 Promise.reject 中传递任何值。你经常在失败的 Promise 中发现 Error 的原因是因为它主要就是用来抛出一个异步错误的。


7. 使用 Promise.all

Javascript 提供了 Promise.all 方法。像 ... 这样的简洁,好吧,我想不出来例子了

在伪算法中,Promise.all 可以被概括为:

接收一个 Promise 数组

    然后同时运行他们

    然后等到他们全部运行完成

    然后 return 一个新的 Promise 数组

    他们其中有一个失败或者 reject,都可以被捕获。

下面的例子展示了所有的 Promise 完成的情况:

var prom1 = Promise.resolve(5);
var prom2 = fetchServerStatus(); // 返回 {statusCode: 200} 的 Promise

Proimise.all([prom1, prom2])
.then([val1, val2] => { // 注意,这里被解析成一个数组
    console.log(val1); // 5
    console.log(val2.statusCode); // 200
})

下面的例子展示了当他们其中一个失败的情况:

var prom1 = Promise.reject(5);
var prom2 = fetchServerStatus(); // 返回 {statusCode: 200} 的 Promise

Proimise.all([prom1, prom2])
.then([val1, val2] => {
    console.log(val1); 
    console.log(val2.statusCode); 
})
.catch(e =>  console.log(e)) // 5, 直接跳转到 .catch

注意:Promise.all 是很聪明的!如果其中一个 Promise 失败了,它不会等到所有的 Promise 完成,而是立即中止!


8. 不要害怕 reject,也不要在每个 .then 后面加冗余的 .catch

我们是不是会经常担心错误会在它们之间的某处被吞噬?

为了克服这个恐惧,这里有一个简单的小提示:

让 reject 来处理上游函数的问题。

在理想的情况下,reject 方法应该是应用的根源,所有的 reject 都会向下传递。

不要害怕像下面这样写

return fetchSomeData(...);

现在如果你想要处理函数中 reject 的情况,请决定是解决问题还是继续 reject。

解决 reject

解决 reject 是很简单的,在 .catch 不论你返回什么内容,都将被假定为已解决的。然而,如果你在 .catch 中返回 Promise.reject,那么这个 Promise 将会是失败的。

.then(() => 5.length) // <-- 这里会报错
.catch(e => {
        return 5;  // <-- 重新使方法正常运行
})
.then(r => {
    console.log(r); // 5
})
.catch(e => {
    console.error(e); // 这个方法永远不会被调用 :)
})

拒绝一个 reject

拒绝一个 reject 是简单的。不需要做任何事情。 就像我刚刚说的,让它成为其他函数的问题。通常情况下,父函数有比当前函数处理 reject 更好的方法。

需要记住的重要的一点是,一旦你写了 catch 方法,就意味着你正在处理这个错误。这个和同步 try/catch的工作方式相似。

如果你确实想要拦截一个 reject:(我强烈建议不要这样做!)

.then(() => 5.length) // <-- 这里会报错
.catch(e => {
  errorLogger(e); // 做一些错误处理
  return Promise.reject(e); // 拒绝它,是的,你可以这么做!
})
.then(r => {
    console.log(r); // 这个 .then (或者任何后面的 .then) 将永远不会被调用,因为我们在上面使用了 reject :)
})
.catch(e => {
    console.error(e); //<-- 它变成了这个 catch 方法的问题
})

.then(x,y) 和 then(x).catch(x) 之间的分界线

.then 接收的第二个回调函数参数也可以用来处理错误。它和 then(x).catch(x) 看起来很像,但是他们处理错误的区别在于他们自身捕获的错误。

我会用下面的例子来说明这一点:

.then(function() {
   return Promise.reject(new Error('something wrong happened'));
}).catch(function(e) {
   console.error(e); // something wrong happened
});

.then(function() {
   return Promise.reject(new Error('something wrong happened'));
}, function(e) { // 这个回调处理来自当前 `.then` 方法之前的错误
    console.error(e); // 没有错误被打印出来
});

当你想要处理的是来自上游 Promise 而不是刚刚在 .then 里面加上去的错误的时候, .then(x,y) 变的很方便。

提示: 99.9% 的情况使用简单的 then(x).catch(x) 更好。


9. 避免 .then 回调地狱

这个提示是相对简单的,尽量避免 .then 里包含 .then 或者 .catch。相信我,这比你想象的更容易避免。

错误的

request(opts)
.catch(err => {
  if (err.statusCode === 400) {
    return request(opts)
           .then(r => r.text())
           .catch(err2 => console.error(err2))
  }
})

正确的

request(opts)
.catch(err => {
  if (err.statusCode === 400) {
    return request(opts);
  }
})
.then(r => r.text())
.catch(err => console.erro(err));

有些时候我们在 .then 里面需要很多变量,那就别无选择了,只能再创建一个 .then 方法链。

.then(myVal => {
    const promA = foo(myVal);
    const promB = anotherPromMake(myVal);
    return promA
          .then(valA => {
              return promB.then(valB => hungryFunc(valA, valB)); // 很丑陋!
          })
})

我推荐使用 ES6 的解构方法混合着 Promise.all 方法就可以解决这个问题。

.then(myVal => {
    const promA = foo(myVal);
    const promB = anotherPromMake(myVal);
    return Promise.all([prom, anotherProm])
})
.then(([valA, valB]) => {   // 很好的使用 ES6 解构
    console.log(valA, valB) // 所有解析后的值
    return hungryFunc(valA, valB)
})

注意:如果你的 node/浏览器/老板/意识允许,还可以使用 async/await 方法来解决这个问题。

我真心希望这篇文章对你理解 Promise 有所帮助。

来自:https://segmentfault.com/a/1190000018089931


链接: https://www.fly63.com/article/detial/1927

你真的了解 Promise 吗?Promise 必知必会(十道题)

Promise 想必大家十分熟悉,想想就那么几个 api,可是你真的了解 Promise 吗?本文根据 Promise 的一些知识点总结了十道题,看看你能做对几道。

剖析Promise内部结构,一步一步实现一个完整的、能通过所有Test case的Promise类

本文写给有一定Promise使用经验的人,如果你还没有使用过Promise,这篇文章可能不适合你,Promise标准中仅指定了Promise对象的then方法的行为,其它一切我们常见的方法/函数都并没有指定.

Async/Await替代Promise的6个理由

Async/Await替代Promise的6个理由:Async/Await是近年来JavaScript添加的最革命性的的特性之一。它会让你发现Promise的语法有多糟糕,而且提供了一个直观的替代方法。

Promise 原理解析与实现(遵循Promise/A+规范)

Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一,Promise 是一个构造函数, new Promise 返回一个 promise对象 接收一个excutor执行函数作为参数

简单模仿实现 Promise 的异步模式

这篇文章是考虑如何自己实现一个简单 Promise,用以理解 Promise。和原生 Promise的调用方法一样,支持链式调用,本文实现的方法只能用于参考Promise的原理,还有很多特性没有实现,比如 race,all 方法的实现。

数组的遍历你都会用了,那Promise版本的呢

在对数组进行一些遍历操作时,发现有些遍历方法对Promise的反馈并不是我们想要的结果。async/await为Promise的语法糖,文中会直接使用async/await替换Promise;map可以说是对Promise最友好的一个函数了,

Promise使用时应注意的问题

最近在使用axios库时遇到了个问题,后端接口报了500错误,但前端并未捕获到。在axios整体配置的代码中,过滤http code时,调用了filter401()、filter500(),但是这里注意并未将两个filter函数的结果返回,也就是并未返回promise,这就是导致问题出现的原因

es6 Promise 的基础用法

想必接触过Node的人都知道,Node是以异步(Async)回调著称的,其异步性提高了程序的执行效率,但同时也减少了程序的可读性。如果我们有几个异步操作,并且后一个操作需要前一个操作返回的数据才能执行

手写一款符合Promise/A+规范的Promise

Promise的一些用法在此不多赘述,本篇主要带领你手写一个Promise源码,学完你就会发现:Promise没有你想象中的那么难.本篇大概分为以下步骤:实现简单的同步Promise、增加异步功能、增加链式调用then、增加catch finally方法、增加all race 等方法、实现一个promise的延迟对象defer、最终测试

Promise 的then 里发生了什么

首先来分析一下then then是属于实例上的方法 参数有2个,分别为onFulfilled, onRejected,并且都是可选的、可以实现链式调用、then执行要执行Promise onFulfilled 或者 onRejected 方法、参数onFulfilled,onRejected 分别有自己的参数, 分别是resolve的参数跟reject的参数、then只能使用前一个then的返回值、then返回值不能是同一个promise

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!