下面部分摘录 We have a problem with promises
每一个 promise 都会提供给你一个 then() 函数 (或是 catch(),实际上只是 then(null, ...) 的语法糖)。当我们在 then() 函数内部时
somePromise().then(function () { // I'm inside a then() function! });
我们可以做什么呢?有三种事情:
就是这样。一旦你理解了这个技巧,你就理解了 promises。因此让我们逐个了解下。
这是一个在 promise 文档中常见的使用模式,也就是我们在上文中提到的 “composing promises”:
getUserByName('nolan').then(function (user) { return getUserAccountById(user.id); }).then(function (userAccount) { // I got a user account! });
注意到我是 return
第二个 promise,这个 return
非常重要。如果我没有写 return
, getUserAccountById()
就会成为一个副作用,并且下一个函数将会接收到 undefined
而非 userAccount
。
返回 undefined 通常是错误的,但是返回一个同步值实际上是将同步代码包裹为 promise 风格代码的一种非常赞的手段。举例来说,我们对 users 信息有一个内存缓存。我们可以这样做:
getUserByName('nolan').then(function (user) { if (inMemoryCache[user.id]) { return inMemoryCache[user.id]; // returning a synchronous value! } return getUserAccountById(user.id); // returning a promise! }).then(function (userAccount) { // I got a user account! });
是不是很赞?第二个函数不需要关心 userAccount 是从同步方法还是异步方法中获取的,并且第一个函数可以非常自由的返回一个同步或者异步值。
不幸的是,有一个不便的现实是在 JavaScript 中无返回值函数在技术上是返回 undefined,这就意味着当你本意是返回某些值时,你很容易会不经意间引入副作用。
出于这个原因,我个人养成了在 then() 函数内部 永远返回或抛出 的习惯。我建议你也这样做。
module.exports = fn => { return (...args) => { return new Promise((resolve, reject) => { args.push((err, result, ...other) => { err ? reject(err) : resolve(result, other) }) fn(...args) }) } }
利用 ES6的新特性,我们仅用10行代码就写出了一个 promisify的函数
我们先写一个用来测试使用的异步函数delay,用来延迟100ms 后返回传进来的数字
const delay = number => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(number) }, 100) }) }
promise 的并行非常的简单,我们可以使用 Promise.all来对一个 Promise 数组进行并行。 下面的代码是对一个数组进行并行执行 delay
// 使用 ava 进行测试 import test from 'ava'; const numbers = [1, 2, 3, 4, 5]; test('parallel', async t => { console.time('parallel'); t.deepEqual(await Promise.all(numbers.map(delay)), numbers); console.timeEnd('parallel'); });
Promise 的串行实现起来比较麻烦(而且我也没想到比较好的应用场景),不过实现 Promise 的串行可以让我们非常好的理解 Promise 本身
Promise 实现的原理是我们要实现一个 Promise 顺序执行串
类似这样
promise1.then(data1 => {return promise2(data1)}).then(data2 => {return promise3(data2)})...
真正的实现如下
// 使用 ava 进行测试 import test from 'ava'; const numbers = [1, 2, 3, 4, 5]; const series = factories => { // 看懂这行你就明白 Promise 了吧 return factories.reduce((resolve, factory) => resolve.then(number => factory()), Promise.resolve()) } const factory = number => () => delay(number) test('serial', async t => { console.time('serial'); t.is(await series(numbers.map(factory)), numbers[numbers.length -1]) console.timeEnd('serial'); })