Last active
June 3, 2017 00:40
-
-
Save mLuby/3f294cc9bf51cc1d8b84c5cd2ae5b41a to your computer and use it in GitHub Desktop.
Implementation of Promises
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"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