Skip to content

Instantly share code, notes, and snippets.

@mLuby
Last active June 3, 2017 00:40
Show Gist options
  • Save mLuby/3f294cc9bf51cc1d8b84c5cd2ae5b41a to your computer and use it in GitHub Desktop.
Save mLuby/3f294cc9bf51cc1d8b84c5cd2ae5b41a to your computer and use it in GitHub Desktop.
Implementation of Promises
"use strict"
// promise constructor and methods
function P (callback) {
let status
let value
const thens = []
const catches = []
setTimeout(() => callback(
data => (status = "resolved", value = processData(data, thens)),
error => (status = "rejected", value = processData(error, catches))
))
const p = {
then: f => (status === "resolved" ? value = f(value) : thens.push(f), p),
catch: f => (status === "rejected" ? value = f(value) : catches.push(f), p)
}
return p
}
P.resolve = data => P(resolve => resolve(data))
P.reject = error => P((resolve, reject) => reject(error))
P.all = ps => P((resolve, reject) => {
const results = []
ps.forEach((p, index) => {
p.then(data => (results[index] = data, sparseLength(results) === ps.length && resolve(results)))
p.catch(reject)
})
})
// helper functions
function processData (x, fs) { return fs.reduce((result, f) => f(result), x) }
function sparseLength (list) { return list.reduce(length => length + 1, 0) }
// tests
const test = require("tape") // npm install tape
test(".then resolves data", t => { t.plan(1)
P((s, j) => s(2))
.then(data => t.equal(data, 2))
})
test(".then chains .then", t => { t.plan(2)
P((s, j) => s(2))
.then(data => (t.equal(data, 2), data * 2))
.then(data => t.equal(data, 4))
})
test(".catch rejects error", t => { t.plan(1)
P((s, j) => j(3))
.catch(error => t.equal(error, 3))
})
test(".then chains .catch", t =>{ t.plan(1)
P((s, j) => j(3))
.then(data => data * 2)
.catch(error => t.equal(error, 3))
})
test("resolving chain can be complex", t =>{ t.plan(1)
P((s, j) => s(2))
.then(data => data + 1)
.then(data => data * 2)
.catch(t.fail)
.then(data => data * data)
.then(data => t.equal(data, 36))
})
test("rejecting chain can be complex", t =>{ t.plan(1)
P((s, j) => j(3))
.then(data => data + 1)
.then(data => data * 2)
.catch(error => t.equal(error, 3))
.then(t.fail)
})
test(".resolve resolves", t =>{ t.plan(1)
P.resolve(2).catch(t.fail)
.then(data => t.equal(data, 2))
})
test(".reject rejects", t =>{ t.plan(1)
P.reject(3).then(t.fail)
.catch(error => t.equal(error, 3))
})
test(".resolve can register and execute .then after executing", t =>{ t.plan(1)
const p = P.resolve(2)
setTimeout(() => p.then(data => t.equal(data, 2)))
})
test(".reject can register and execute .catch after executing", t =>{ t.plan(1)
const p = P.reject(3)
setTimeout(() => p.catch(error => t.equal(error, 3)))
})
test(".all resolves after all promises resolve", t => { t.plan(1)
const p1 = P.resolve(1)
const p2 = P.resolve(2)
P.all([p1, p2]).then(data => t.deepEqual(data, [1, 2])).catch(t.fail)
})
test(".all rejects as soon as one promise rejects", t => { t.plan(1)
const p1 = P.resolve(1)
const p2 = P.reject(3)
const p3 = P.resolve(2)
P.all([p1, p2, p3]).then(t.fail)
.catch(error => t.equal(error, 3))
})
test("order of execution is correct", t => { t.plan(6)
let order = 0
t.equal(++order, 1)
const p = new Promise(resolve => {
t.equal(++order, 2)
resolve(6)
t.equal(++order, 3)
})
t.equal(++order, 4)
p.then(data => t.equal(++order, 6))
t.equal(++order, 5)
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment