• <sub id="xcyjv"></sub><nav id="xcyjv"><code id="xcyjv"><meter id="xcyjv"></meter></code></nav>
      <nav id="xcyjv"></nav>
    1. <form id="xcyjv"><th id="xcyjv"></th></form><nav id="xcyjv"><mark id="xcyjv"></mark></nav>

        Learn JavaScript promises in about 70 minutes

        by qntm

        Because twenty-five other explanations weren't enough, here I will explain JavaScript promises. This tutorial is intended for people who already understand JavaScript.

        Previous educational essays of mine are Perl in 2 hours 30 minutes and regular expressions in 55 minutes.

        Contents

        The problem

        JavaScript is single-threaded. It will only ever do at most one thing at once. There is no concurrency.

        For example, if x is a global variable of some kind, then it is impossible for the value of x to change between these two lines of code:

        console.log(x);
        console.log(x);
        

        This is because there are no other threads which could modify x while these two lines are executing.

        *

        JavaScript is event-based. It maintains a queue of messages. "A function is associated with each message. When the [call] stack is empty, a message is taken out of the queue and processed. The processing consists of calling the associated function (and thus creating an initial stack frame). The message processing ends when the stack becomes empty again."

        We do not have the ability to inspect the queue, reorder it or remove messages from it.

        Typically in JavaScript a message takes (or should take) an extremely small amount of time to process, of the order of milliseconds. This ensures that the application continues to appear responsive. If a message takes a very long time to process, or forever:

        while(true) { }
        

        then no other messages can be processed, and the application becomes unresponsive. Commonly this leaves us with no choice but to kill the application entirely (e.g. by closing the browser tab).

        *

        Now suppose we want to make a HTTP request. What we would like to write is something like this:

        // BAD CODE DO NOT USE
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "some/resource.json", false);
        xhr.send();
        
        console.log(xhr.responseText);
        

        This is how we make a synchronous HTTP request. The call to xhr.send() blocks until the HTTP request is completed, after which we are free to inspect the XMLHttpRequest object to see the response.

        But if we do this, then every other message gets blocked up behind this HTTP request. If the server takes a noticeable amount of time to respond, then the application stutters. If the server never responds, then the application locks up forever. Code like this is strongly discouraged due to the negative effect it has on the user experience.

        Instead, we must write code asynchronously.

        // GOOD CODE USE THIS INSTEAD
        var xhr = new XMLHttpRequest();
        
        xhr.addEventListener("load", function() {
        	console.log(this.responseText);
        });
        
        xhr.open("GET", "some/resource.json");
        xhr.send();
        

        Here, the xhr.send() call returns immediately, and JavaScript continues working, while carrying out the real HTTP request in the background (i.e. on a thread to which we, the JavaScript programmer, do not have programmatic access). This is called non-blocking I/O.

        Later, when (if) the HTTP request is completed, a new message will be placed in the message queue. The message will be associated with that listener function:

        function() {
        	console.log(this.responseText);
        }
        

        and, when JavaScript eventually reaches that message in the queue, the function will be called.

        *

        Now, how can we turn this HTTP request-making code into a callable function? What we want to write in our calling code is something like:

        var responseText = get("some/resource.json");
        

        But neither of these approaches works:

        var get = function(resource) {
        	var xhr = new XMLHttpRequest();
        	xhr.addEventListener("load", function() {
        		return this.responseText; // this value goes nowhere
        	});
        	xhr.open("GET", resource);
        	xhr.send();
        	return xhr.responseText; // returns `undefined`
        };
        

        The solution is callbacks. When calling get, we pass in our URL but also a callback function. When — at some nebulous future time — get has finished its task, it will call that function, passing the result in.

        var get = function(resource, callback) {
        	var xhr = new XMLHttpRequest();
        	xhr.addEventListener("load", function() {
        		callback(this.responseText);
        	});
        	xhr.open("GET", resource);
        	xhr.send();
        };
        

        Usage:

        get("some/resource.json", function(responseText) {
        	console.log(responseText);
        });
        

        This is called continuation-passing style. As a general pattern, this works well, but it is not very pretty. It means that all later code has to be placed inside, or called from, that callback.

        *

        Interestingly, this pattern can even find use in functions which would not normally be asynchronous. A function like:

        var parseJson = function(json) {
        	var obj = JSON.parse(json);
        	return obj;
        };
        

        becomes:

        var parseJson = function(json, callback) {
        	setTimeout(function() {
        		var obj = JSON.parse(json);
        		callback(obj);
        	}, 0);
        };
        

        Here, setTimeout() is simply putting a message on the queue right away, without any delay. Although the interval specified is 0 milliseconds, this does not mean that

        function() {
        	var obj = JSON.parse(json);
        	callback(obj);
        }
        

        will be invoked immediately; other messages which have been queued up in the meantime will be handled first. We might do this if we are carrying out a long single calculation and we want to break it up into smaller messages.

        Note that parseJson no longer explicitly returns anything, which is another way of saying that it returns undefined. The same is true of get and any other function employing this pattern.

        *

        This pattern of callbacks becomes troublesome when we need to carry out several tasks in sequence:

        get("some/resource.json", function(responseText) {
        	parseJson(responseText, function(obj) {
        		extractFnord(obj, function(fnord) {
        			console.log(fnord);
        		});
        	});
        });
        

        Note the increasingly severe indentation. This is called callback hell.

        The worse problem

        How do we handle errors in this scenario? There are several ways to do this, but they both tend to make the situation even less readable.

        Asynchronous error handling technique 1

        One technique for handling errors is this:

        var get = function(resource, callback) {
        	var xhr = new XMLHttpRequest();
        	xhr.addEventListener("load", function() {
        		if(this.status === 200) {
        			callback(undefined, this.responseText);
        		} else {
        			callback(Error());
        		}
        	});
        	xhr.addEventListener("error", function() {
        		callback(Error());
        	});
        	xhr.open("GET", resource);
        	xhr.send();
        };
        

        And:

        var parseJson = function(json, callback) {
        	setTimeout(function() {
        		try {
        			var obj = JSON.parse(json);
        			callback(undefined, obj);
        		} catch(e) {
        			callback(e);
        		}
        	}, 0);
        };
        

        Here we pass two arguments to the callback, the first of which is an error. Customarily, if nothing goes wrong, then err is falsy.

        Usage:

        get("some/resource.json", function(err1, responseText) {
        	if(err1) {
        		console.error(err1);
        		return;
        	}
        	parseJson(responseText, function(err2, obj) {
        		if(err2) {
        			console.error(err2);
        			return;
        		}
        		extractFnord(obj, function(err3, fnord) {
        			if(err3) {
        				console.error(err3);
        				return;
        			}
        			console.log(fnord);
        		});
        	});
        });
        

        This approach is seen very commonly. As a prime example, take a look at the fs module in Node.js, which carries out filesystem operations, another form of I/O. Compare this blocking I/O function:

        var str = fs.readFileSync("example.txt", "utf8");
        console.log(str);
        

        with this non-blocking version:

        fs.readFile("example.txt", "utf8", function(err, str) {
        	if(err) {
        		console.error(err);
        		return;
        	}
        	console.log(str);
        });
        

        Asynchronous error handling technique 2

        The other way to handle errors is to pass two callbacks into the calculation. One is for success, and the other is for failure:

        var get = function(resource, callback, errback) {
        	var xhr = new XMLHttpRequest();
        	xhr.addEventListener("load", function() {
        		if(this.status === 200) {
        			callback(this.responseText);
        		} else {
        			errback(Error());
        		}
        	});
        	xhr.addEventListener("error", function() {
        		errback(Error());
        	});
        	xhr.open("GET", resource);
        	xhr.send();
        };
        

        And:

        var parseJson = function(json, callback, errback) {
        	setTimeout(function() {
        		try {
        			var obj = JSON.parse(json);
        			callback(obj);
        		} catch(e) {
        			errback(e);
        		}
        	}, 0);
        };
        

        Usage:

        get("some/resource.json", function(responseText) {
        	parseJson(responseText, function(obj) {
        		extractFnord(obj, function(fnord) {
        			console.log(fnord);
        		}, function(err3) {
        			console.error(err3);
        		});
        	}, function(err2) {
        		console.error(err2);
        	});
        }, function(err1) {
        	console.error(err1);
        });
        

        This is marginally better, but still pretty ghastly. Both approaches have drawbacks and some inflexibility, as well as bloating argument lists. There must be a better way!

        Introducing promises

        Let's do some refactoring. Rewrite get like so:

        var get = function(resource) {
        	return new Promise(function(resolve, reject) {
        		var xhr = new XMLHttpRequest();
        		xhr.addEventListener("load", function() {
        			if(this.status === 200) {
        				resolve(this.responseText);
        			} else {
        				reject(Error());
        			}
        		});
        		xhr.addEventListener("error", function() {
        			reject(Error());
        		});
        		xhr.open("GET", resource);
        		xhr.send();
        	});
        };
        

        And similarly parseJson:

        var parseJson = function(json) {
        	return new Promise(function(resolve, reject) {
        		setTimeout(function() {
        			try {
        				var obj = JSON.parse(json);
        				resolve(obj);
        			} catch(e) {
        				reject(e);
        			}
        		}, 0);
        	});
        };
        

        The asynchronous functions no longer accept callbacks, either for success or for failure. But they do return something now. The object returned is called a promise. The promise represents the task which get or parseJson (or extractFnord) has promised to do. It has three states: pending, fulfilled or rejected.

        A promise starts out pending. The inner function(resolve, reject) { ... }, which we supply, is called the executor callback. The promise calls this function, passing in two arguments, resolve and reject. When the task has been done, we call resolve to fulfill the promise i.e. mark the task as completed. Or, if the task has failed, we call reject to reject the promise i.e. mark the task as failed. A promise which is fulfilled or rejected is called settled.

        By interacting with this promise object, we can register success and error callbacks to be called when it settles. But first, some bullet points!

        Most of these little guarantees just serve to make promises simpler and more robust in their behaviour. Notice that there were no such guarantees in our original asynchronous functions definitions; there was nothing to stop us from, say, calling callback twice with two different values. Promises protect us from such weirdness and from the boilerplate code which would be required to manually handle such weirdness.

        And also introducing then()

        Now here's a very important point: there is no way to directly inspect the current state of a promise, or the value it fulfilled with (if any), or the error it rejected with (if any).

        Instead, we call then(callback, errback) on a promise to register a success callback and an error callback. And so our usage now looks like this:

        get("some/resource.json").then(function(responseText) {
        	parseJson(responseText).then(function(obj) {
        		extractFnord(obj).then(function(fnord) {
        			console.log(fnord);
        		}, function(err3) {
        			console.error(err3);
        		});
        	}, function(err2) {
        		console.error(err2);
        	});
        }, function(err1) {
        	console.error(err1);
        });
        

        This still isn't great, but we're gradually getting closer to something good. First, more bullet points:

        Promise chaining

        Calling then(callback, errback) on a promise returns a new promise. The way in which the new promise settles depends on two things:

        1. The way in which the first promise settles.
        2. What happens inside the success or error callback.

        Here are some examples.

        *

        In a success or error callback, we can return or throw nearly any value we like. If we don't return anything, this is the same as returning undefined, so the fulfilled value of the new promise is undefined, which is fine. There's one special case, which we may remember from earlier:

        If we fulfill a promise with a second promise, the first promise settles the same way as the second

        This applies no matter what method we use to fulfill that first promise, be it Promise.resolve():

        Promise.resolve("foo")                                   // fulfills with "foo"
        Promise.reject("bar")                                    // rejects with "bar"
        Promise.resolve(Promise.resolve("foo"))                  // fulfills with "foo"
        Promise.resolve(Promise.reject("bar"))                   // rejects with "bar"
        Promise.resolve(Promise.resolve(Promise.resolve("foo"))) // fulfills with "foo"
        Promise.resolve(Promise.resolve(Promise.reject("bar")))  // rejects with "bar"
        // and so on
        

        Or the executor callback:

        new Promise(function(resolve, reject) { resolve("foo"); })                  // fulfills with "foo"
        new Promise(function(resolve, reject) { reject("bar"); })                   // rejects with "bar"
        new Promise(function(resolve, reject) { resolve(Promise.resolve("foo")); }) // fulfills with "foo"
        new Promise(function(resolve, reject) { resolve(Promise.reject("foo")); })  // rejects with "bar"
        // and so on
        

        Or a success or error callback:

        Promise.resolve().then(function() { return "foo"; })                  // fulfills with "foo"
        Promise.resolve().then(function() { throw "bar"; })                   // rejects with "bar"
        Promise.resolve().then(function() { return Promise.resolve("foo"); }) // fulfills with "foo"
        Promise.resolve().then(function() { return Promise.reject("bar"); })  // rejects with "bar"
        // and so on
        

        (Note that it's totally okay to reject a promise with a second promise:

        Promise.reject(Promise.resolve("foo"))                                      // rejects with `Promise.resolve("foo")`
        new Promise(function(resolve, reject) { reject(Promise.resolve("foo")); }); // rejects with `Promise.resolve("foo")`
        Promise.resolve().then(function() { throw Promise.resolve("foo"); })        // rejects with `Promise.resolve("foo")`
        // and so on
        

        But this is a rather odd thing to do...)

        *

        Why is this so significant?

        Because it means that we can asynchronously transform values as well. Which allows us to suddenly turn this code:

        get("some/resource.json").then(function(responseText) {
        	parseJson(responseText).then(function(obj) {
        		extractFnord(obj).then(function(fnord) {
        			console.log(fnord);
        		}, function(err3) {
        			console.error(err3);
        		});
        	}, function(err2) {
        		console.error(err2);
        	});
        }, function(err1) {
        	console.error(err1);
        });
        

        into this:

        get("some/resource.json").then(function(responseText) {
        	return parseJson(responseText);
        }).then(function(obj) {
        	return extractFnord(obj);
        }).then(function(fnord) {
        	console.log(fnord);
        }).catch(function(err) {
        	console.error(err);
        });
        

        And boom! We're out of callback hell!

        *

        Let's break our new asynchronous code down. There are five promises in the main chain.

        *

        One more thing. Since the callback functions are guaranteed to be called with only a single argument, and the value of this passed will be undefined, and our intermediate functions parseJson and extractFnord don't use this internally, our code may be simplified even further:

        get("some/resource.json")
        	.then(parseJson)
        	.then(extractFnord)
        	.then(function(fnord) {
        		console.log(fnord);
        	}).catch(function(err) {
        		console.error(err);
        	});
        

        In some browsers, console.log and console.error aren't sensitive to the value of this either, so we can even go as far as:

        get("some/resource.json")
        	.then(parseJson)
        	.then(extractFnord)
        	.then(console.log)
        	.catch(console.error);
        

        Amazing!

        To take maximum advantage of this pattern, write functions (and methods!) which

        1. Accept only a single argument
        2. Do not use this (or, use this but also use bind() to fix its value)
        3. Return a promise

        *

        So here's a fun edge case. If we fulfill a promise with a second promise, the first promise settles the same way as the second. What if the second promise is the first promise?

        var p = new Promise(function(resolve, reject) {
        	setTimeout(function() {
        		resolve(p);
        	}, 0);
        });
        

        (Note that setTimeout must be used here, since the executor callback is called synchronously at Promise construction time, at which time the value of p is still undefined.)

        Answer: this promise rejects with a TypeError because of the cycle that has been introduced.

        Let's use this to segue into the topic of error handling.

        Error handling in a promise chain

        If a promise rejects, execution passes to the next available error callback. To demonstrate how this works, we'll start with a basic promise chain:

        Promise.resolve("foo").then(function(str) {
        	return str + str;
        }).then(function(str) {
        	console.log(str);
        }, function(err) {
        	console.error(err);
        });
        

        and see what happens if we introduce errors — which is to say, cause promises to reject — at various points.

        This code:

        Promise.reject("bar").then(function(str) {
        	return str + str;
        }).then(function(str) {
        	console.log(str);
        }, function(err) {
        	console.error(err);
        });
        

        hits none of the success callbacks, and immediately errors out printing "bar".

        This code:

        Promise.resolve("foo").then(function(str) {
        	throw str + str;
        }).then(function(str) {
        	console.log(str);
        }, function(err) {
        	console.error(err);
        });
        

        hits the first success callback, then errors out printing "foofoo". The second success callback is not hit.

        And finally, this code:

        Promise.resolve("foo").then(function(str) {
        	return str + str;
        }).then(function(str) {
        	throw str;
        }, function(err) {
        	console.error(err);
        });
        

        prints nothing at all!

        Remember: when we use then(callback, errback), either the success callback or the error callback will be called, never both. errback does not handle exceptions thrown by callback. callback and errback are both intended to handle the outcome from the previous promise, not from each other.

        Because of this potential for "leaking" an error, I think it is good practice to never call then(callback, errback), passing in both callbacks. It's safer to always use then(callback), passing in only one callback, or, when handling errors at the tail end of a chain, to use catch(errback):

        Promise.resolve("foo").then(function(str) {
        	return str + str;
        }).then(function(str) {
        	throw str;
        }).catch(function(err) {
        	console.error(err);
        });
        

        And in general, we should always conclude a promise chain with a catch() call, because otherwise errors in the chain will disappear and never be detected.

        Of course, if an exception is thrown during an error callback, then we may be out of luck entirely, but that's a standing problem with all of error handling...

        Implementation variations

        Throughout this document we have been using the native Promise implementation which is present in many JavaScript engines. This is a relatively new feature of JavaScript and does not have universal support; in particular, it is not available in Internet Explorer. However, there are numerous third-party implementations of promises which work in a basically identical way, such as Q and Bluebird.

        All these implementations conform to a technical specification called Promises/A+. This specification only really specifies the behaviour of the then() method. The then() method will work identically in all conforming implementations.

        Everything else is left up to implementers. For example, the APIs for:

        are not specified by Promises/A+. The native Promise implementation specified in ES6 works like this, and other implementations generally work similarly, but these APIs are not necessarily universal.

        Different implementations are also at liberty to offer whatever additional functionality they wish. Promise offers two other methods worth mentioning, Promise.all() and Promise.race(), both of which accept an array of promises run "in parallel". Promise.all() fulfills with an array containing the fulfilled values from all the inner promises, Promise.race() fulfills with the value of the first inner promise to fulfill. Other implementations usually offer equivalent functionality and often offer much more functionality.

        Summary of promise settling behaviour

        Promise Attempted settle method Value/error New state Value/error
        new Promise(function(resolve, reject) { resolve( "foo" ); }) Fulfilled "foo"
        new Promise(function(resolve, reject) { resolve( Promise.resolve("X") ); }) Fulfilled "X"
        new Promise(function(resolve, reject) { resolve( Promise.reject("Y") ); }) Rejected "Y"
        new Promise(function(resolve, reject) { reject( "foo" ); }) Rejected "foo"
        new Promise(function(resolve, reject) { reject( Promise.resolve("X") ); }) Rejected Promise.resolve("X")
        new Promise(function(resolve, reject) { reject( Promise.reject("Y") ); }) Rejected Promise.reject("Y")
        new Promise(function(resolve, reject) { return  "foo" ; }) Pending none
        new Promise(function(resolve, reject) { return  Promise.resolve("X") ; }) Pending none
        new Promise(function(resolve, reject) { return  Promise.reject("Y") ; }) Pending none
        new Promise(function(resolve, reject) { throw  "foo" ; }) Rejected "foo"
        new Promise(function(resolve, reject) { throw  Promise.resolve("X") ; }) Rejected Promise.resolve("X")
        new Promise(function(resolve, reject) { throw  Promise.reject("Y") ; }) Rejected Promise.reject("Y")
        Promise.resolve().then(function() { resolve( "foo" ); }) Rejected ReferenceError("resolve is not defined")
        Promise.resolve().then(function() { resolve( Promise.resolve("X") ); }) Rejected ReferenceError("resolve is not defined")
        Promise.resolve().then(function() { resolve( Promise.reject("Y") ); }) Rejected ReferenceError("resolve is not defined")
        Promise.resolve().then(function() { reject( "foo" ); }) Rejected ReferenceError("reject is not defined")
        Promise.resolve().then(function() { reject( Promise.resolve("X") ); }) Rejected ReferenceError("reject is not defined")
        Promise.resolve().then(function() { reject( Promise.reject("Y") ); }) Rejected ReferenceError("reject is not defined")
        Promise.resolve().then(function() { return  "foo" ; }) Fulfilled "foo"
        Promise.resolve().then(function() { return  Promise.resolve("X") ; }) Fulfilled "X"
        Promise.resolve().then(function() { return  Promise.reject("Y") ; }) Rejected "Y"
        Promise.resolve().then(function() { throw  "foo" ; }) Rejected "foo"
        Promise.resolve().then(function() { throw  Promise.resolve("X") ; }) Rejected Promise.resolve("X")
        Promise.resolve().then(function() { throw  Promise.reject("Y") ; }) Rejected Promise.reject("Y")

        Conclusion

        I find promises to be heck of complicated and it wasn't until I sat down and tried to write this that I realised just how poorly I understood them. Not pictured here is the lengthy interlude where I gave up and, as a learning exercise, attempted to read, understand and implement the Promises/A+ specification myself, which nearly caused my head to explode. Anyway, I think I have a pretty good handle on them now, and I hope you do too.

        Back to Things Of Interest

        01彩票网app
      1. <sub id="xcyjv"></sub><nav id="xcyjv"><code id="xcyjv"><meter id="xcyjv"></meter></code></nav>
          <nav id="xcyjv"></nav>
        1. <form id="xcyjv"><th id="xcyjv"></th></form><nav id="xcyjv"><mark id="xcyjv"></mark></nav>
            金昌 | 海宁 | 广元 | 崇左 | 南京 | 朔州 | 衢州 | 石狮 | 济南 | 陇南 | 中卫 | 姜堰 | 淮南 | 包头 | 高雄 | 黑龙江哈尔滨 | 嘉峪关 | 长垣 | 北海 | 定西 | 固原 | 金坛 | 武夷山 | 宁波 | 武夷山 | 日喀则 | 玉环 | 恩施 | 象山 | 鄢陵 | 临沂 | 台中 | 张掖 | 丽江 | 鹤壁 | 象山 | 达州 | 吉林长春 | 滕州 | 张家界 | 榆林 | 晋中 | 南安 | 哈密 | 柳州 | 铁岭 | 吕梁 | 东方 | 泉州 | 福建福州 | 巴音郭楞 | 永州 | 海西 | 朝阳 | 马鞍山 | 博尔塔拉 | 防城港 | 莱州 | 晋城 | 瓦房店 | 白山 | 鸡西 | 琼海 | 临沧 | 五家渠 | 沧州 | 喀什 | 阿克苏 | 西藏拉萨 | 阳春 | 延边 | 巴彦淖尔市 | 锡林郭勒 | 甘肃兰州 | 昆山 | 盐城 | 德宏 | 单县 | 遂宁 | 甘南 | 张掖 | 淮北 | 三河 | 甘孜 | 本溪 | 溧阳 | 湘潭 | 克孜勒苏 | 章丘 | 山南 | 青州 | 黔东南 | 鄂州 | 吴忠 | 云浮 | 景德镇 | 菏泽 | 寿光 | 灌南 | 迪庆 | 怒江 | 东阳 | 包头 | 巴彦淖尔市 | 漯河 | 亳州 | 三门峡 | 巴中 | 肥城 | 锦州 | 宜宾 | 邹城 | 泗阳 | 淮南 | 吉林长春 | 抚州 | 江西南昌 | 兴化 | 库尔勒 | 荆州 | 海安 | 吐鲁番 | 台北 | 锡林郭勒 | 溧阳 | 建湖 | 玉溪 | 姜堰 | 德清 | 厦门 | 甘南 | 德阳 | 柳州 | 扬中 | 钦州 | 莱芜 | 六盘水 | 攀枝花 | 巴中 | 新余 | 茂名 | 承德 | 永康 | 福建福州 | 潮州 | 文昌 | 东莞 | 承德 | 黔东南 | 青州 | 澳门澳门 | 澳门澳门 | 台湾台湾 | 保定 | 自贡 | 丹阳 | 赵县 | 红河 | 海宁 | 莱芜 | 大同 | 四平 | 台州 | 汕尾 | 防城港 | 宜春 | 江西南昌 | 邯郸 | 沧州 | 大庆 | 平潭 | 石狮 | 湛江 | 内蒙古呼和浩特 | 黔南 | 齐齐哈尔 | 榆林 | 铜仁 | 朝阳 | 温州 | 上饶 | 曲靖 | 保山 | 荣成 | 桐乡 | 肥城 | 海安 | 黄冈 | 汉川 | 黄南 | 普洱 | 达州 | 毕节 | 齐齐哈尔 | 洛阳 | 平顶山 | 阜新 | 大理 | 大庆 | 娄底 | 南通 | 西双版纳 | 桐城 | 吴忠 | 遵义 | 长治 | 溧阳 | 延安 | 固原 | 达州 | 汕头 | 资阳 | 任丘 | 泗洪 | 建湖 | 蚌埠 | 日喀则 | 广饶 | 遵义 | 安徽合肥 | 广西南宁 | 阿勒泰 | 东台 | 吉林 | 温岭 | 潮州 | 灌云 | 鹰潭 | 扬中 | 龙口 | 景德镇 | 宝应县 | 宜都 | 曲靖 | 丹东 | 荆门 | 博尔塔拉 | 陇南 | 亳州 | 白沙 | 北海 | 揭阳 | 灵宝 | 吉安 | 平潭 | 武威 | 东台 | 吉林 | 佳木斯 | 临沧 | 呼伦贝尔 | 山东青岛 | 丹东 | 齐齐哈尔 | 汕尾 | 梅州 | 四川成都 | 攀枝花 | 乌兰察布 | 定安 | 海安 | 天水 | 济宁 | 邵阳 | 福建福州 | 阳江 | 鹤壁 | 单县 | 济南 | 塔城 | 鹤壁 | 海丰 | 兴化 | 鞍山 | 桓台 | 台山 | 大连 | 吴忠 | 涿州 | 台州 | 长兴 | 泰兴 | 平凉 | 玉林 | 高密 | 潍坊 | 锡林郭勒 | 张家口 | 马鞍山 | 滁州 | 新乡 | 汉川 | 保山 | 阿克苏 | 邢台 | 昭通 | 深圳 | 广饶 | 吴忠 | 临汾 | 三明 | 南安 | 永康 | 温州 | 巴中 | 温岭 | 榆林 | 杞县 | 巢湖 | 金坛 | 阜新 | 杞县 | 阜新 | 临沂 | 盐城 | 益阳 | 西双版纳 | 雄安新区 | 池州 | 萍乡 | 宜昌 | 桂林 | 琼中 | 贺州 | 黄山 | 昌吉 | 临夏 | 吉林 | 孝感 | 鄂尔多斯 | 天门 | 鞍山 | 宿迁 | 漯河 | 广饶 | 廊坊 | 安岳 | 汉川 | 雄安新区 | 宜昌 | 恩施 | 临汾 | 盐城 | 梧州 | 毕节 | 临猗 | 琼海 | 广西南宁 | 广州 | 茂名 | 兴安盟 | 株洲 | 广饶 | 莆田 | 临汾 | 黑河 | 滁州 | 萍乡 | 玉树 | 邹平 | 乐平 | 广西南宁 | 屯昌 | 聊城 | 池州 | 贵港 | 镇江 | 鹤岗 | 淮北 | 燕郊 | 海宁 | 垦利 | 海南 | 昆山 | 湘西 | 雄安新区 | 吴忠 | 鄂州 | 德阳 | 河北石家庄 | 晋江 | 中山 | 济源 | 菏泽 | 汉中 | 仁寿 | 泗洪 | 天长 | 鄂州 | 河源 | 新沂 | 内江 | 来宾 | 枣阳 | 六安 | 湘西 | 霍邱 | 秦皇岛 | 通辽 | 伊春 | 安顺 | 朝阳 | 霍邱 | 福建福州 | 延安 | 建湖 | 柳州 | 金坛 | 诸城 | 图木舒克 | 清徐 | 内蒙古呼和浩特 | 海北 | 海南 | 高密 | 遂宁 | 阿克苏 | 青海西宁 | 任丘 | 仙桃 | 三门峡 | 巴中 | 扬中 | 那曲 | 桐乡 | 海西 | 临沂 | 邹平 | 张家口 | 洛阳 | 仁怀 | 武夷山 | 海丰 | 澄迈 | 呼伦贝尔 | 溧阳 | 西双版纳 | 扬州 | 玉树 | 荆州 | 宁国 | 武安 | 遵义 | 商丘 | 朝阳 | 神木 | 东台 | 长葛 | 平潭 | 泉州 | 延边 | 铁岭 | 海丰 | 仁寿 | 连云港 | 兴安盟 | 涿州 | 玉林 | 三河 | 溧阳 | 七台河 | 达州 | 三明 | 慈溪 | 定西 | 梅州 | 湖南长沙 | 淮南 | 澳门澳门 | 江苏苏州 | 单县 | 衡阳 | 阿拉尔 | 五指山 | 广安 | 贵州贵阳 | 永新 | 枣阳 | 黑河 | 朔州 | 章丘 | 神木 | 伊犁 | 林芝 | 昌吉 | 茂名 | 包头 | 大丰 | 通辽 | 惠东 | 天水 | 灌南 | 泗洪 | 德州 | 潜江 | 舟山 | 四川成都 | 贺州 | 遂宁 | 大理 | 沛县 | 江西南昌 | 泰兴 | 十堰 | 济源 | 宝应县 | 和田 | 廊坊 | 包头 | 六安 | 揭阳 | 章丘 | 西双版纳 | 桓台 | 厦门 | 三沙 | 阳春 | 六盘水 | 北海 | 那曲 | 深圳 | 咸宁 | 内江 | 防城港 | 巢湖 | 德清 | 灌南 | 赣州 | 镇江 | 周口 | 河源 | 阿克苏 | 运城 | 陇南 | 楚雄 | 丹阳 | 枣阳 | 和县 | 淮安 | 临沂 | 兴安盟 | 沛县 | 福建福州 | 台中 | 承德 | 石狮 | 台北 | 南安 | 库尔勒 | 兴化 | 六安 | 泰州 | 临汾 | 包头 | 洛阳 | 鄂尔多斯 | 辽阳 | 曲靖 | 佛山 | 烟台 | 澳门澳门 | 衡阳 | 宁夏银川 | 海西 | 阜新 | 黔南 | 燕郊 | 承德 | 崇左 | 雄安新区 | 葫芦岛 | 宿州 | 包头 | 台南 | 灌云 | 日照 | 绥化 | 图木舒克 | 防城港 | 马鞍山 | 保定 | 黔东南 | 梧州 | 丹阳 | 乐清 | 五家渠 | 吉安 | 七台河 | 义乌 | 乐清 | 红河 | 保山 | 乌兰察布 | 章丘 | 澳门澳门 | 青海西宁 | 池州 | 天水 | 普洱 | 嘉峪关 | 锦州 | 济南 | 临沧 | 邹平 | 东莞 | 连云港 | 鸡西 | 贵港 | 漳州 | 云南昆明 | 伊犁 | 巢湖 | 顺德 | 江门 | 阳江 | 章丘 | 衡水 | 阿勒泰 | 宁德 | 枣庄 | 鄢陵 | 新泰 | 滨州 | 中卫 | 涿州 | 吴忠 | 莒县 | 宜春 | 天水 | 长葛 | 永康 | 霍邱 | 陇南 | 青海西宁 | 德清 | 许昌 | 芜湖 | 三亚 | 安康 | 定安 | 桂林 | 濮阳 | 天水 | 上饶 | 任丘 | 山西太原 | 临夏 | 吉林长春 | 巴音郭楞 | 义乌 | 双鸭山 | 舟山 | 福建福州 | 眉山 | 鞍山 | 汉川 | 赣州 | 湖北武汉 | 日喀则 | 寿光 | 沧州 | 鹤岗 | 任丘 | 包头 | 澄迈 | 雅安 | 遵义 | 随州 | 荆门 | 巴音郭楞 | 衡水 | 安吉 | 诸暨 | 邳州 | 怒江 | 深圳 | 德清 | 宁夏银川 | 绵阳 | 荆州 | 迁安市 | 酒泉 | 本溪 | 朔州 | 山东青岛 | 攀枝花 | 赵县 | 甘肃兰州 | 武安 | 建湖 | 铜陵 | 商洛 | 霍邱 | 铜陵 | 巢湖 | 仁寿 | 包头 | 绍兴 | 乌海 | 通化 | 阳江 | 湖州 | 双鸭山 | 桂林 | 常州 | 衡阳 | 营口 | 邢台 | 濮阳 | 迪庆 | 漯河 | 孝感 | 德清 | 内江 | 温州 | 临猗 | 沭阳 | 三沙 | 济南 | 柳州 | 正定 | 龙口 | 衡水 | 神木 | 自贡 | 日喀则 | 儋州 | 白银 | 漳州 | 眉山 | 大理 | 仁寿 | 丹阳 | 海南 | 东营 | 阿克苏 | 菏泽 | 燕郊 | 许昌 | 内蒙古呼和浩特 | 襄阳 | 海门 | 宁国 | 阿里 | 鸡西 | 枣庄 | 廊坊 | 博罗 | 随州 | 杞县 | 广元 | 昌吉 | 贵港 | 阿坝 | 玉林 | 云浮 | 自贡 | 南平 | 台南 | 常德 | 大兴安岭 | 昌都 | 永新 | 南安 | 铁岭 | 白银 | 潍坊 | 包头 | 大庆 | 福建福州 | 岳阳 | 德州 | 六盘水 | 日照 | 宁国 | 许昌 | 贺州 | 朔州 | 临海 | 黄山 | 扬州 | 漯河 | 遵义 | 济源 | 大庆 | 营口 | 娄底 | 如东 | 九江 | 伊春 | 马鞍山 | 天门 | 威海 | 雅安 | 文昌 | 燕郊 | 镇江 | 开封 | 肇庆 | 义乌 | 临沧 | 宜昌 | 安岳 | 海东 | 阿拉尔 | 齐齐哈尔 | 惠州 | 黄南 | 海西 | 潮州 | 龙口 | 扬中 | 商丘 | 泉州 | 张家口 | 鹤岗 | 蓬莱 | 济南 | 台中 | 平潭 | 香港香港 | 江苏苏州 | 深圳 | 和田 | 包头 | 龙口 | 库尔勒 | 永康 | 定州 | 深圳 | 乐清 | 吉林 | 遵义 | 昌吉 | 北海 | 公主岭 | 安顺 | 东莞 | 溧阳 | 石狮 | 大同 | 嘉兴 | 信阳 | 白沙 | 中山 | 新泰 | 营口 | 临沧 | 定州 | 濮阳 | 临猗 | 顺德 | 新沂 | 海门 | 海丰 | 莆田 | 巢湖 | 平潭 | 东营 | 齐齐哈尔 | 香港香港 | 鄂州 | 海南 | 庆阳 | 吐鲁番 | 醴陵 | 海拉尔 | 黄山 | 张家界 | 丽江 | 岳阳 | 龙岩 | 保山 | 鞍山 | 梧州 | 汕尾 | 乐平 | 黔东南 | 顺德 | 三门峡 | 扬州 | 定西 | 大连 | 肥城 | 巴音郭楞 | 石河子 | 陵水 | 那曲 | 姜堰 | 邵阳 | 漳州 | 日喀则 | 东莞 | 松原 | 临沧 | 白银 | 汕尾 | 涿州 | 东台 | 陵水 | 桐乡 | 中卫 | 武夷山 | 金坛 | 台湾台湾 | 珠海 | 宿州 | 乌海 | 南阳 | 慈溪 | 揭阳 | 庄河 | 汉中 | 吐鲁番 | 宜春 | 改则 | 项城 | 仙桃 | 铜仁 | 琼海 | 铁岭 | 景德镇 | 韶关 | 包头 | 三沙 | 七台河 | 余姚 | 普洱 | 赵县 | 东营 | 三亚 | 招远 | 平凉 | 高雄 | 泰安 | 伊犁 | 宁夏银川 | 赤峰 | 临沧 | 曲靖 | 台山 | 揭阳 | 汉中 | 大庆 | 福建福州 | 昭通 | 商洛 | 葫芦岛 | 赵县 | 韶关 | 永州 | 南充 | 黄南 | 云南昆明 | 汝州 | 孝感 | 贵州贵阳 | 长治 | 东阳 | 海丰 | 牡丹江 | 安阳 | 香港香港 | 四川成都 | 亳州 | 兴安盟 | 九江 | 丹东 | 林芝 | 余姚 | 灌云 | 郴州 | 神木 | 象山 | 鹤岗 | 昌吉 | 石狮 | 汝州 | 永州 | 淄博 | 昭通 | 迁安市 | 汉川 | 宜春 | 金昌 | 公主岭 | 海东 | 临沧 | 公主岭 | 固原 | 燕郊 | 安庆 | 大庆 | 德清 | 资阳 | 莒县 | 邢台 | 淄博 | 长治 | 荆门 | 三亚 | 乐平 | 和县 | 上饶 | 温岭 | 晋城 | 唐山 | 长垣 | 常德 | 新乡 | 廊坊 | 巴彦淖尔市 | 高雄 | 万宁 | 佛山 | 武威 | 哈密 | 南京 | 漳州 | 眉山 | 甘南 | 泰安 | 乌海 | 威海 | 曲靖 | 南充 | 海东 | 招远 | 云浮 | 芜湖 | 沧州 | 吴忠 | 文山 | 江西南昌 | 宜春 | 如皋 | 赣州 | 丽江 | 周口 | 安徽合肥 | 吴忠 | 阳江 | 厦门 | 泉州 | 玉溪 | 枣庄 | 义乌 | 库尔勒 | 迪庆 | 焦作 | 海东 | 图木舒克 | 克孜勒苏 | 基隆 | 余姚 | 霍邱 | 绍兴 | 崇左 | 赣州 | 达州 | 平顶山 | 铜仁 | 安岳 | 辽宁沈阳 | 营口 | 呼伦贝尔 | 博尔塔拉 | 潍坊 | 正定 | 日照 | 沛县 | 舟山 | 曲靖 | 庆阳 | 平顶山 | 鹤壁 | 四平 | 定西 | 六安 | 吕梁 |