Writes out the ReactElements to the response. Returns a promise that fulfills when
all the ReactElements have been written out.
function writeBody(req, res, context, start, page) {

	// standardize to an array of EarlyPromises of ReactElements
	var elementPromises = PageUtil.standardizeElements(page.getElements());

	// This is where we'll store our rendered HTML strings.  A value of
	// `undefined` means we haven't rendered that element yet.
	var rendered = elementPromises.map(() => ELEMENT_PENDING);

	// We need to return a promise that resolves when we're done, so we'll
	// maintain an array of deferreds that we punch out as we render
	// elements and we'll return a promise that resolves when they've all
	// been hit.
	var dfds = elementPromises.map(() => Q.defer());

	var doElement = (element, index) => {

		// Exceeded `FAILSAFE_RENDER_TIMEOUT`.  Bummer.
		if (rendered[index] === ELEMENT_ALREADY_WRITTEN) return;

		rendered[index] = renderElement(res, element, context);

		// If we've just rendered the next element to be written we'll
		// write it out.
		writeElements(res, rendered);

		dfds[index].resolve();
	};

	// Render elements as their data becomes available.
	elementPromises.forEach((promise, index) => promise
		.then(element => doElement(element, index))
		.catch(e => {
			logger.error(`Error rendering element ${index}`, e)
			// TODO: the error handling here should probably be merged
			// somehow with renderElement so that we get timing info.

			// In the case where there was an exception thrown while rendering,
			// the next three lines are effectively a no-op. In the case where
			// the element promise was rejected, this prevents a hang until
			// FAILSAFE_RENDER_TIMEOUT has passed.

			// No way we can recover in the second case, so let's just move on.
			// We'll call `writeElements` just in case everything is ready
			// after us.

			// This doesn't completely handle the extremely unlikely case that:
			//     1) `renderElement` successfully rendered this element, and
			//     2) `writeElements` successfully wrote it, but...
			//     3) `writeElements` threw after this element was written.
			//
			// We'll make a good-faith effort, but in this rare case writeElements is probably
			// going to fail again when we call it here. At least if that happens, _this_
			// particular element should show up properly on the page, even though the page
			// overall could be totally horked. And we won't have a 20s timeout...
			try {
				if (rendered[index] !== ELEMENT_ALREADY_WRITTEN) {
					rendered[index] = '';
					writeElements(res, rendered);
				}
			} finally {
				// try _really_ hard to resolve this deferred, to avoid a 20s hang.
				dfds[index].resolve();
			}
		})
		// just in case writeElements throws in our error callback above.
		.catch(e => logger.error(`Error recovering from error rendering element ${index}`, e))
	);

	// Some time has already elapsed since the request started.
	// Note that you can override `FAILSAFE_RENDER_TIMEOUT` with a
	// `?_debug_render_timeout={ms}` query string parameter.
	var totalWait     = DebugUtil.getRenderTimeout() || FAILSAFE_RENDER_TIMEOUT
		,   timeRemaining = totalWait - (new Date - start)

	var retval = Q.defer();
	var writeBodyDfd = Q.defer();

	// If we exceed the timeout then we'll just send empty elements for
	// anything that hadn't rendered yet.
	writeBodyDfd.promise.catch((err) => {

		// Write out what we've got.
		writeElements(res, rendered.map(
			value => value === ELEMENT_PENDING?'':value
		));

		// If it hasn't arrived by now, we're not going to wait for it.
		RLS().lateArrivals = undefined;

		// Let the client know it's not getting any more data.
		renderScriptsAsync([{ text: `__reactServerClientController.failArrival()` }], res)

		//Log timeout error but still resolve so we continue in the lifecycle process
		logger.error("Error in writeBody", err);
		retval.resolve();
	});

	Q.all(dfds.map(dfd => dfd.promise)).then(writeBodyDfd.resolve);

	const timeout = setTimeout(() => {
		// give some additional information when we time out
		writeBodyDfd.reject({
			message: "Timed out rendering.",
			// `timeRemaining` is how long we waited before timing out
			timeWaited: timeRemaining,
			elements: rendered.map(val => {
				if (val === ELEMENT_ALREADY_WRITTEN) {
					return 'W'; // written
				} else if (val === ELEMENT_PENDING) {
					return 'P'; // not rendered
				} else {
					return 'R'; // rendered, not yet written
				}
			}),
		});
	}, timeRemaining);

	// Don't leave dead timers hanging around.
	writeBodyDfd.promise.then(() => {
		clearTimeout(timeout);
		//writeBody ran successfully, sweet
		retval.resolve();
	});

	return retval.promise;
}