Broken Promises

ES6 Biggest ๐ŸŽ

Promises

A little bit of history

In computer science, future, promise, delay, and deferred refer to constructs used for synchronizing program execution in some concurrent programming languages. They describe an object that acts as a proxy for a result that is initially unknown, usually because the computation of its value is not yet complete.

The term promise was proposed in 1976 by Daniel P. Friedman and David Wise,[1] and Peter Hibbard called it eventual

in the beginning of 2011, Promise concepts were made popular by jQuery Deferred Objects.

โ€œThe Promise object is used for deferred and asynchronous computations. A Promise represents an operation that hasnโ€™t completed yet, but is expected in the future.โ€ โ€” MDN Promise Reference

Promise State

Pending

Fullfilled

Rejected

????????

Mind Games ๐Ÿง ๐ŸŽฎ

What is the state of

const test = new Promise((resolve, reject) => {
  resolve('Yay!');
});

Mind Games ๐Ÿง ๐ŸŽฎ

What is the state of

const test = new Promise((resolve, reject) => {
  reject('Nay!');
});

Mind Games ๐Ÿง ๐ŸŽฎ

What is the state of

const test = new Promise((resolve, reject) => {
  throw new Error('Yay?');
});

Mind Games ๐Ÿง ๐ŸŽฎ

What is the state of

const test = new Promise((resolve, reject) => {
  resolve('Yay!');
  throw new Error('Huh?');
});

Mind Games ๐Ÿง ๐ŸŽฎ

What is the state of

const test = new Promise((resolve, reject) => {
  resolve('Yay!');
  console.log('Did I get executed?');
});

Mind Games ๐Ÿง ๐ŸŽฎ

What is the state of

const test = new Promise((resolve, reject) => {
  resolve('Yay!');
  console.log('Did I get executed?');
});

Mind Games ๐Ÿง ๐ŸŽฎ

What is the state of

const test = new Promise((resolve, reject) => {
  return;
  resolve('Yay?');
  console.log('Did I get executed?');
});

Mind Games ๐Ÿง ๐ŸŽฎ

What is the state of

const test = new Promise((resolve, reject) => {
  resolve('Yay!');
  reject('Sorry ๐Ÿ˜… I meant nay!');
});

Mind Games ๐Ÿง ๐ŸŽฎ

What is the state of

const test = new Promise((resolve, reject) => {
  resolve('Yay!');
}).then(value => {
  throw new Error(value);
}).catch(err => {
  console.log('error!');
});

Mind Games ๐Ÿง ๐ŸŽฎ

How many times will it be logged

var p = new Promise((resolve, reject) => {
  reject(Error('Can you fail twice?'))
})
p.catch(error => console.log(error.message))
p.catch(error => console.log(error.message))

IRL Demo

const generateQueryId = e.request
  .clone()
  .json()
  .then(({ query, variables }) => {
    // ....
  });

const networkResponse = fromNetwork(e.request);

e.respondWith(
  (async () => {
    // ....
    generateQueryId.then(queryId => {
      // ... now I have the query id
    })
 
    // ... Returning the network response
    return networkResponse.then(res => res.clone());
  })()
);

e.waitUntil(
  (async () => {
    // ....
    // re-using the network response
    networkResponse.then(res => res.clone()).then(() => {
      // ... do more stuff
    });
    generateQueryId.then(queryId => {
      // ... now I have the query id again
    })
    // ....
  })()
);

Mind Games ๐Ÿง ๐ŸŽฎ

How many times will it be logged

const p = new Promise((resolve, reject) => {
  return Promise.reject(Error('Can I fail twice!'))
})
p.catch(error => console.log(error.message))
p.catch(error => console.log(error.message))

Callback Hell ๐Ÿ”ฅ

fs.readdir(source, function (err, files) {
  if (err) {
    console.log('Error finding files: ' + err)
  } else {
    files.forEach(function (filename, fileIndex) {
      console.log(filename)
      gm(source + filename).size(function (err, values) {
        if (err) {
          console.log('Error identifying file size: ' + err)
        } else {
          console.log(filename + ' : ' + values)
          aspect = (values.width / values.height)
          widths.forEach(function (width, widthIndex) {
            height = Math.round(width / aspect)
            console.log('resizing ' + filename + 'to ' + height + 'x' + height)
            this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
              if (err) console.log('Error writing file: ' + err)
            })
          }.bind(this))
        }
      })
    })
  }
})

Refactored ๐Ÿคทโ€โ™‚๏ธ

fs.readdir(source).catch(err => {
  console.log('Error finding files: ' + err)
}).then(files => {
  files.forEach(function (filename, fileIndex) {
    gm(source + filename).size().catch(err => {
      console.log('Error identifying file size: ' + err)
    }).then(values => {
     console.log(filename + ' : ' + values)
      aspect = (values.width / values.height)
      widths.forEach(function (width, widthIndex) {
        height = Math.round(width / aspect)
        console.log('resizing ' + filename + 'to ' + height + 'x' + height)
        this.resize(width, height).write(dest + 'w' + width + '_' + filename).catch(err => {
          console.log('Error writing file: ' + err)
        });
      }) 
    });
  })
});
  

Chainable Promises

function getUserAddress () {
  return new Promise(resolve => {
    // MAKE API CALL...
  });
}

function getUserEmail () {
  return new Promise(resolve => {
    // MAKE API CALL...
  });
}

function makeOrder(email, address) {
  return new Promise(resolve => {
    // MAKE API CALL...
  });
}

// before!
getUserEmail().then(email => {  
  getUserAddress().then(address => {
   	userData.address = address;
    
    makeOrder(email, address).then(response => {
      // ... more work
    })
  });
});

Chainable Promises

function getUserAddress () {
  return new Promise(resolve => {
    // MAKE API CALL...
  });
}

function getUserEmail () {
  return new Promise(resolve => {
    // MAKE API CALL...
  });
}

function makeOrder(email, address) {
  return new Promise(resolve => {
    // MAKE API CALL...
  });
}

// before!
getUserEmail().then(email => {
  return getUserAddress().then(address => ({ email, address }));
}).then(userData => {
  return makeOrder(userData.email, userData.address);
}).then(response => {
  // Single level of indentation!
});
fetch(uri, {
  method: 'post',
  body: JSON.stringify({
    query: print(query),
    variables
  })
}).catch(err => {
  throw new Error(err);
})
.then(res => res.json())
.then(data => {
  
})

Promise Queue

Throttling promises one at a time

let queue = Promise.resolve();
function runInQueue(fn) {
  queue = queue.then(() => fn());
}

runInQueue(() => Promise.resolve('Yay!'));
runInQueue(() => makeAPICall());
runInQueue(() => makeAPICall());
runInQueue(() => makeAPICall());
runInQueue(() => makeAPICall());

Promise Sequencing

const queue = [];
function runInQueue(fn) {
  queue.push(fn);
}

function startQueue() {
  return queue.reduce((prev, curr) => {
    return prev.then(() => curr());
  }, Promise.resolve());
}

// Prepare Some Requests!
runInQueue(() => makeAPICall());
runInQueue(() => makeAPICall());
runInQueue(() => makeAPICall());
runInQueue(() => makeAPICall());

// Run them in sequeuence!
startQueue();

IRL Demo

const queue = new PQueue({ concurrency: 1 });

function performSafeCartUpdate({ action, commit }) {
  const handler = async ({ data, errors }) => {
    if (errors) {
      // eslint-disable-next-line no-console
      console.error(errors[0].message);
    }

    const items = data.response.cart.items.map(mapCartItem);
    // Sync the Cart back!
    commit('SET_ITEMS', items);
    commit('SET_SYNC_DATE', new Date());
    await saveToIDB('cart', items);
  };

  queue.add(() =>
    action().then(res => {
      if (!queue.size) {
        return handler(res);
      }
    })
  );
}

Parallel Promises

Promise.all([
  makeAPICall(),
  makeAPICall(),
  makeAPICall(),
  makeAPICall()
]).then(([res1, res2, res3, res4]) => {
  // ...
});

Async/Await

async function getUserEmail() {
  // ...
}

async function getUserAddress() {
  // ...
}

const address = await getUserAddress();
const email = await getUserEmail();

Identify the 2 problems here ๐Ÿค”

async function getUserEmail() {
  // ...
}

async function getUserAddress() {
  // ...
}

(async () => {
  const [email, address] = await Promise.all([
    getUserEmail(),
    getUserAddress()
  ]);
});

Fixed ๐Ÿ”ง

async function getUserEmail () {
  // ...
}

What does`async`even do?

function getUserEmail () {
  return new Promise(resolve => {
    // ...
    // resolve(result)
  });
}
const result = await someAsyncCode();

And what does`await`do?

someAsyncCode().then(result => {
  // Result!
});
function fn () {
  return new Promise.resolve(10);
}

const result = await fn();

Can you `await` non-async functions?

function fn () {
  return 10;
}


// is this legal?
const result = await fn();
function sleep(wait) {
  return new Promise(resolve => {
    setTimeout(resolve, wait);
  });
}

(async () => {
  for (let i = 1; i <= 5; i++) {
    await sleep(1000);
    console.log('Slept for ' + 1000 * i);
  }
})();

Semantic Differences in loops

function sleep(wait) {
  return new Promise(resolve => {
    setTimeout(resolve, wait);
  });
}

[1, 2, 3, 4, 5].forEach(async i => {
   await sleep(1000);
  console.log('Slept for ' + 1000 * i);
});

Semantic Differences in loops

Q: When do you don't want to await?

A: When you don't want to wait

async logout({ commit, dispatch }) {
  Cookies.remove('auth');
  commit('SET_TOKEN', '');
  commit('SET_USER', null);
  await dispatch('cart/clear', undefined, { root: true });

  // Don't wait for this, revoking token in background!
  this.$api.mutate({
    mutation: RevokeToken
  });
}

IRL Example

const networkResponse = fromNetwork(e.request);

e.respondWith(
  (async () => {
    // get the request body.
    const queryId = await generateQueryId;
    const cachedResult = queryId && (await fromCache(queryId));
    if (cachedResult) {
      return cachedResult;
    }

    return networkResponse.then(res => res.clone());
  })()
);

e.waitUntil(
  (async () => {
    try {
      const res = await networkResponse.then(res => res.clone());
      const queryId = await generateQueryId;
      if (queryId) {
        await saveToCache(queryId, res);
      }
    } catch (err) {
      // eslint-disable-next-line no-console
      console.log(err);
    }
  })()
);

IRL Example

Thanks! ๐Ÿ‘‹

Fulfilled and Broken Promises

By Abdelrahman Awad

Fulfilled and Broken Promises

  • 867