Out with async/await and in with Promise Lifting

栏目: IT技术 · 发布时间: 6年前

Promise Lifting: What and When to Use

A new way to write asynchronous code

Out with async/await and in with Promise Lifting

Jan 16 ·6min read

Out with async/await and in with Promise Lifting

Blue and Red Superman Print Tank Top Shirt

Promise vs Array

Before we dive into Promise Lifting, we need to take a look at and understand what Promise is. I like comparing Promises to Arrays because of their many similarities (and because most people have a good understanding of Arrays)

Promises and Arrays are both containers for values and they both have transformation functions map vs then/catch .

Let’s see some of these similarities written with code:

They Hold Values

Array and Promise are containers that hold value(s).

Out with async/await and in with Promise Lifting

cats in boxes (Promise and Array)
const array = ['Hello Array']
const promise = Promise.resolve('Hello Promise')

They Have Transformation Functions

The then method on a Promise functions similarly to map on an Array . They both transform the value(s) and returns a new Array / Promise .

Out with async/await and in with Promise Lifting

cats in boxes with .map and .then transformations
const toUpper = text => text.toUpperCase()array.map(toUpper)
promise.then(toUpper)

The Value(s) Can Be Unboxed

The value in an Array can be pulled out by access the index [0] or using await on a Promise .

Out with async/await and in with Promise Lifting

cat being unboxed

This is an important comparison as I will be discussed in the next section.

const helloArray = array[0]
const helloPromise = await promise

Tip:Use Bit ( Github ) to “harvest” React/Angular/Vue components from any codebase and share them on bit.dev . Let your team reuse and collaborate on components to write scalable code, speed up development and maintain a consistent UI.

Out with async/await and in with Promise Lifting
Example: searching for shared React components in bit.dev

So What Is The Point?

The point is simple. If you saw me write code like this, you would (rightfully) slap me in the face.

const toUpperArray = array => {
  // unbox the value
  const text = array[0]  // transform the value
  const upper = text.toUpperCase()  // box it back up
  return [upper]
}

Why did we pull the value out of the Array , just to put it back an the Array ?

But that is how we write code with async / await . We unbox the value then box it back into a Promise when the function returns.

// this is a contrived function representative of a lot of
// async/await code.const toUpperPromise = async promise => {
  // unbox the value
  const text = await promise  // transform the value
  const upper = text.toUpperCase()  // box it back up
  return upper
}

toUpperPromise returns upper wrapped up in a resolved Promise .

Leave it in The Box

It doesn’t make much sense to unbox the value and then put it back a the box when the values can be transformed (without unboxing) by using map or then / catch .

Out with async/await and in with Promise Lifting

cat in a box
const toUpper = text => text.toUpperCase()array.map(toUpper)
promise.then(toUpper)

2+ Arity Functions

.map and .then are amazing functions when working with one value at a time.

The “leave it in the box” solution gets complicated when you get into 2+ arity (argument) functions.

const add = (x, y) => x + yconst array1 = [3]
const array2 = [4]const value = array1.flatMap(x => array2.map(y => add(x, y)))
// [7]

It’s not much better with a Promise either.

const add = (x, y) => x + yconst promise1 = Promise.resolve(3)
const promise2 = Promise.resolve(4)const value = promise1.then(x => promise2.then(y => add(x, y)))

Quick recap:

  • 1 arity solution good :+1:
  • 2+ arity solution bad :-1:

The async/await Solution

await makes it easy to unbox a value from a Promise . But because our function is now async , the value will be boxed back up into a Promise .

const add = (x, y) => x + yconst compute = async () => {
const value1 = await Promise.resolve(3)
const value2 = await Promise.resolve(4)
return add(value1, value2)
}

This is better than the 2+ arity solutions, but we are still unboxing values and reboxing them again.

What I Want

If I got my wish, it would be to be able to write asynchronous code in a way that is similar to this synchronous code block.

const add = (x, y) => x + yconst value1 = 3
const value2 = 4const result = add(value1, value2)

Promise Lifting can help us get there.

Promise Lifting

The term lift comes from functional programming, which in turn got the term from mathematics. Everything is math. You can think of a lift as a transformation to another type.

So far all of the solutions have been focusing on transforming the data . But what if instead we flip this and we transformed the function ?

Let’s lift the add function from number to Promise<number> and see what the code looks like.

/**
 * @param {number} x
 * @param {number} x
 * @returns {number}
 */
const add = (x, y) => x + y/**
 * @param {Promise<number>} x
 * @param {Promise<number>} x
 * @returns {Promise<number>}
 */
const addP = liftP(add)const promise1 = Promise.resolve(3)
const promise2 = Promise.resolve(4)const promiseValue = addP(promise1, promise2)

So in this example, add is wrapped with the liftP function (decorator). This converts the function from one that accepts numbers as arguments to a function that accepts Promises as arguments .

Now we no longer need to unbox the values! And if you look again at the synchronous example, that is EXACTLY how our code works!

const add = (x, y) => x + yconst value1 = 3
const value2 = 4const result = add(value1, value2)
Out with async/await and in with Promise Lifting
head exploding

Wow! We are writing asynchronous code, but we are writing it the same way we write synchronous code!

Another Example

Let’s take some synchronous code. Ya I know this code is junk. It’s a contrived example to so the liftP has something to lift.

It’s just two functions. It could be any two functions.

const getComparisonText = (being1, being2) => {
if (being1.height === being2.height)
return 'the same height'
if (Number(being1.height) > Number(being2.height))
return 'taller than'
return 'shorter than'
}
const compareHeight = (being1, being2) => {
const compareText = getComparisonText(being1, being2)
return `${being1.name} is ${compareText} as ${being2.name}`
}

Now lift the compareHeight and call it compareHeightP . I add an P to the end so I know this version takes Promises.

const compareHeightP = liftP(compareHeight)

Now just call the function the same way you would if the code was synchronous.

const luke = httpGet('https://swapi.co/api/people/1/?format=json')
const c3po = httpGet('https://swapi.co/api/people/2/?format=json')compareHeightP(luke, c3po)
// Promise("Luke Skywalker is taller than as C-3PO")

The Code

The liftP function is a short one-liner. The P on the end there is for Promise . This is so the function does not get confused with the liftA or liftA2 functions (for Applicatives). But that’s for another time.

const liftP = func => (...args) =>
Promise.all(args).then(values => func(...values))

Play Time

Press run below to start playing with the code:

(start reading at line 29)

Summary

  • Promise Lifting changes the focus from converting our values to work with our functions, to converting the functions to work with our values .
  • Promise Lifted code appears structurally similar to synchronous code .
  • Boxed values are no longer unboxed and then reboxed.

Am I recommending converting all your async / await code to Promise Lifted code? Absolutely not.

There are valid places to use a Promise, async / await as well as a Promise Lift.

This is just one more tool in your box to choose from.

Out with async/await and in with Promise Lifting

Cheers!

Follow the discussion on Twitter: @joelnet

Learn More


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

设计模式之禅(第2版)

设计模式之禅(第2版)

秦小波 / 机械工业出版社 / 2014-2-25 / 89.00元

本书是设计模式领域公认的3本经典著作之一,“极具趣味,容易理解,但讲解又极为严谨和透彻”是本书的写作风格和方法的最大特点。第1版2010年出版,畅销至今,广受好评,是该领域的里程碑著作。深刻解读6大设计原则和28种设计模式的准确定义、应用方法和最佳实践,全方位比较各种同类模式之间的异同,详细讲解将不同的模式组合使用的方法。第2版在第1版的基础上有两方面的改进,一方面结合读者的意见和建议对原有内容中......一起来看看 《设计模式之禅(第2版)》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具