做网站 教程,在线教育网站建设,南宁网站建设nnxun,网站都有后台吗目录
手写promise
同步版
1.Promise的构造方法接收一个executor()#xff0c;在new Promise()时就立刻执行executor回调
2.executor()内部的异步任务被放入宏/微任务队列#xff0c;等待执行
3.状态与结果的管理
状态只能变更一次
4.then()调用成功/失败回调
catch是…目录
手写promise
同步版
1.Promise的构造方法接收一个executor()在new Promise()时就立刻执行executor回调
2.executor()内部的异步任务被放入宏/微任务队列等待执行
3.状态与结果的管理
状态只能变更一次
4.then()调用成功/失败回调
catch是调用失败回调的简写
异步版
1.缓存成功与失败回调
2.then 增加 Pending处理
3.resolve 与 reject 中调用回调函数
多次调用同一个promise的then
1.缓存成功与失败回调 队列
2.pengding时then()收集依赖将成功/失败回调放入成功/失败队列
3.触发resolve/reject从成功/失败队列中取出回调依次执行
then链式调用返回一个 Promise 对象
then返回自己时抛错循环调用
等返回的promise初始化好queueMicrotask微任务
捕获错误
executor错误
then错误
then([onFulfilled, onRejected])参数可选
then 穿透忽略非函数参数非函数会同步执行
静态调用resolve、reject
完整版
Promise A 规范版的resolvePromise
catch
finally
并发请求
模板
all
allSettled
any
race 手写promise
同步版 1.将promise的resolve和reject函数传给实例用 constructor(executor){// executor 是一个执行器进入会立即执行// 并传入resolve和reject方法executor(this.resolve, this.reject) } 2.实例给resolve和reject函数传值 resolve(success)
reject(err) // 新建 test.js// 引入我们的 MyPromise.js
const MyPromise require(./MyPromise)
const promise new MyPromise((resolve, reject) {resolve(success)reject(err)
})promise.then(value {console.log(resolve, value)
}, reason {console.log(reject, reason)
})// 执行结果resolve success1.Promise的构造方法接收一个executor()在new Promise()时就立刻执行executor回调
class Promise{// 构造方法接收一个回调constructor(executor){executor();}
2.executor()内部的异步任务被放入宏/微任务队列等待执行 // resolve和reject为什么要用箭头函数 // 如果直接调用的话普通函数this指向的是window或者undefined // 用箭头函数就可以让this指向当前实例对象
class MyPromise {constructor(executor){// executor 是一个执行器进入会立即执行// 并传入resolve和reject方法executor(this.resolve, this.reject) }// 更改成功后的状态resolve () {}// 更改失败后的状态reject () {}
}3.状态与结果的管理
状态只能变更一次
// 先定义三个常量表示状态
const PENDING pending;
const FULFILLED fulfilled;
const REJECTED rejected;// 新建 MyPromise 类
class MyPromise {constructor(executor){...}// 储存状态的变量初始值是 pendingstatus PENDING;// 成功之后的值value null;// 失败之后的原因reason null;// 更改成功后的状态resolve (value) {// 只有状态是等待才执行状态修改if (this.status PENDING) {// 状态修改为成功this.status FULFILLED;// 保存成功之后的值this.value value;}}// 更改失败后的状态reject (reason) {// 只有状态是等待才执行状态修改if (this.status PENDING) {// 状态成功为失败this.status REJECTED;// 保存失败后的原因this.reason reason;}}
}
4.then()调用成功/失败回调
catch是调用失败回调的简写
// MyPromise.jsthen(onFulfilled, onRejected) {// 判断状态if (this.status FULFILLED) {// 调用成功回调并且把值返回onFulfilled(this.value);} else if (this.status REJECTED) {// 调用失败回调并且把原因返回onRejected(this.reason);}
}异步版
// test.jsconst MyPromise require(./MyPromise)
const promise new MyPromise((resolve, reject) {setTimeout(() {resolve(success)}, 2000);
})promise.then(value {console.log(resolve, value)
}, reason {console.log(reject, reason)
})// 同步版没有打印信息执行到then时状态还是pending
// 异步版等待 2s 输出 resolve success1.缓存成功与失败回调
// MyPromise 类中新增
// 存储成功回调函数
onFulfilledCallback null;
// 存储失败回调函数
onRejectedCallback null;2.then 增加 Pending处理
// MyPromise.jsthen(onFulfilled, onRejected) {...if (this.status PENDING) {// 新增 // 因为不知道后面状态的变化情况所以将成功回调和失败回调存储起来// 等到执行成功失败函数的时候再传递this.onFulfilledCallback onFulfilled;this.onRejectedCallback onRejected;}
}3.resolve 与 reject 中调用回调函数
// MyPromise.js// 更改成功后的状态
resolve (value) {// 只有状态是等待才执行状态修改if (this.status PENDING) {// 状态修改为成功this.status FULFILLED;// 保存成功之后的值this.value value;// 新增 // 判断成功回调是否存在如果存在就调用this.onFulfilledCallback this.onFulfilledCallback(value);}
}多次调用同一个promise的then
// test.jsconst MyPromise require(./MyPromise)
const promise new MyPromise((resolve, reject) {setTimeout(() {resolve(success)}, 2000);
})promise.then(value {console.log(1)console.log(resolve, value)
})promise.then(value {console.log(2)console.log(resolve, value)
})promise.then(value {console.log(3)console.log(resolve, value)
})
//单个回调3
resolve success
//回调队列
1
resolve success
2
resolve success
3
resolve success
1.缓存成功与失败回调 队列
// MyPromise.js// 存储成功回调函数
// onFulfilledCallback null;
onFulfilledCallbacks [];
// 存储失败回调函数
// onRejectedCallback null;
onRejectedCallbacks [];2.pengding时then()收集依赖将成功/失败回调放入成功/失败队列
// MyPromise.jsthen(onFulfilled, onRejected) {// 判断状态if (this.status FULFILLED) {// 调用成功回调并且把值返回onFulfilled(this.value);} else if (this.status REJECTED) {// 调用失败回调并且把原因返回onRejected(this.reason);} else if (this.status PENDING) {// 新增 // 因为不知道后面状态的变化这里先将成功回调和失败回调存储起来// 等待后续调用this.onFulfilledCallbacks.push(onFulfilled);this.onRejectedCallbacks.push(onRejected);}
}3.触发resolve/reject从成功/失败队列中取出回调依次执行
// MyPromise.js// 更改成功后的状态
resolve (value) {// 只有状态是等待才执行状态修改if (this.status PENDING) {// 状态修改为成功this.status FULFILLED;// 保存成功之后的值this.value value;// 新增 // resolve里面将所有成功的回调拿出来执行while (this.onFulfilledCallbacks.length) {// Array.shift() 取出数组第一个元素然后调用shift不是纯函数取出后数组将失去该元素直到数组为空this.onFulfilledCallbacks.shift()(value)}}
}then链式调用返回一个 Promise 对象
以fulfilled为例其他同理
// MyPromise.jsclass MyPromise {...then(onFulfilled, onRejected) {// 为了链式调用这里直接创建一个 MyPromise并在后面 return 出去const promise2 new MyPromise((resolve, reject) {// 这里的内容在执行器中会立即执行if (this.status FULFILLED) {// 获取成功回调函数的执行结果const x onFulfilled(this.value);// 传入 resolvePromise 集中处理resolvePromise(x, resolve, reject);} ...}) return promise2;}
}function resolvePromise(x, resolve, reject) {// 判断x是不是 MyPromise 实例对象if(x instanceof MyPromise) {// 执行 x调用 then 方法目的是将其状态变为 fulfilled 或者 rejected// x.then(value resolve(value), reason reject(reason))// 简化之后x.then(resolve, reject)} else{// 普通值resolve(x)}
}then返回自己时抛错循环调用
// test.jsconst promise new Promise((resolve, reject) {resolve(100)
})
const p1 promise.then(value {console.log(value)return p1
})function resolvePromise(promise2, x, resolve, reject) {// 如果相等了说明return的是自己抛出类型错误并返回if (promise2 x) {return reject(new TypeError(Chaining cycle detected for promise #Promise))}...
}等返回的promise初始化好queueMicrotask微任务 // MyPromise.jsclass MyPromise {......then(onFulfilled, onRejected) {const promise2 new MyPromise((resolve, reject) {if (this.status FULFILLED) {// 创建一个微任务等待 promise2 完成初始化queueMicrotask(() {// 获取成功回调函数的执行结果const x onFulfilled(this.value);// 传入 resolvePromise 集中处理resolvePromise(promise2, x, resolve, reject);}) } ...}) return promise2;}
}捕获错误 try {异步操作
} catch (error) {reject(error)
}
executor错误
// MyPromise.jsconstructor(executor){// 新增 // executor 是一个执行器进入会立即执行// 并传入resolve和reject方法try {executor(this.resolve, this.reject)} catch (error) {// 如果有错误就直接执行 rejectthis.reject(error)}
}then错误
// MyPromise.jsthen(onFulfilled, onRejected) {// 为了链式调用这里直接创建一个 MyPromise并在后面 return 出去const promise2 new MyPromise((resolve, reject) {// 判断状态if (this.status FULFILLED) {// 创建一个微任务等待 promise2 完成初始化queueMicrotask(() {try {// 获取成功回调函数的执行结果const x onFulfilled(this.value);// 传入 resolvePromise 集中处理resolvePromise(promise2, x, resolve, reject);} catch (error) {reject(error)} }) } ...}) return promise2;
}then([onFulfilled, onRejected])参数可选
then 穿透忽略非函数参数非函数会同步执行
Promise.resolve(1).then(2)//传入值.then(Promise.resolve(3))//传入promise对象.then(console.log)//传入函数
1Promise.resolve().then(new Promise(r {setTimeout(() {r(console.log(1))}, 1000)})).then(new Promise(r {setTimeout(() {r(console.log(2))}, 1000)})).then(new Promise(r {setTimeout(() {r(console.log(3))}, 1000)}))
延迟1秒后打印123
不同于下面
// MyPromise.jsthen(onFulfilled, onRejected) {// 如果不传就使用默认函数onFulfilled typeof onFulfilled function ? onFulfilled : value value;onRejected typeof onRejected function ? onRejected : reason {throw reason};// 为了链式调用这里直接创建一个 MyPromise并在后面 return 出去const promise2 new MyPromise((resolve, reject) {......
}
静态调用resolve、reject
// MyPromise.jsMyPromise {......// resolve 静态方法static resolve (parameter) {// 如果传入 MyPromise 就直接返回if (parameter instanceof MyPromise) {return parameter;}// 转成常规方式return new MyPromise(resolve {resolve(parameter);});}// reject 静态方法static reject (reason) {return new MyPromise((resolve, reject) {reject(reason);});}
}完整版
// MyPromise.js// 先定义三个常量表示状态
const PENDING pending;
const FULFILLED fulfilled;
const REJECTED rejected;// 新建 MyPromise 类
class MyPromise {constructor(executor){// executor 是一个执行器进入会立即执行// 并传入resolve和reject方法try {executor(this.resolve, this.reject)} catch (error) {this.reject(error)}}// 储存状态的变量初始值是 pendingstatus PENDING;// 成功之后的值value null;// 失败之后的原因reason null;// 存储成功回调函数onFulfilledCallbacks [];// 存储失败回调函数onRejectedCallbacks [];// 更改成功后的状态resolve (value) {// 只有状态是等待才执行状态修改if (this.status PENDING) {// 状态修改为成功this.status FULFILLED;// 保存成功之后的值this.value value;// resolve里面将所有成功的回调拿出来执行while (this.onFulfilledCallbacks.length) {// Array.shift() 取出数组第一个元素然后调用shift不是纯函数取出后数组将失去该元素直到数组为空this.onFulfilledCallbacks.shift()(value)}}}// 更改失败后的状态reject (reason) {// 只有状态是等待才执行状态修改if (this.status PENDING) {// 状态成功为失败this.status REJECTED;// 保存失败后的原因this.reason reason;// resolve里面将所有失败的回调拿出来执行while (this.onRejectedCallbacks.length) {this.onRejectedCallbacks.shift()(reason)}}}then(onFulfilled, onRejected) {const realOnFulfilled typeof onFulfilled function ? onFulfilled : value value;const realOnRejected typeof onRejected function ? onRejected : reason {throw reason};// 为了链式调用这里直接创建一个 MyPromise并在后面 return 出去const promise2 new MyPromise((resolve, reject) {const fulfilledMicrotask () {// 创建一个微任务等待 promise2 完成初始化queueMicrotask(() {try {// 获取成功回调函数的执行结果const x realOnFulfilled(this.value);// 传入 resolvePromise 集中处理resolvePromise(promise2, x, resolve, reject);} catch (error) {reject(error)} }) }const rejectedMicrotask () { // 创建一个微任务等待 promise2 完成初始化queueMicrotask(() {try {// 调用失败回调并且把原因返回const x realOnRejected(this.reason);// 传入 resolvePromise 集中处理resolvePromise(promise2, x, resolve, reject);} catch (error) {reject(error)} }) }// 判断状态if (this.status FULFILLED) {fulfilledMicrotask() } else if (this.status REJECTED) { rejectedMicrotask()} else if (this.status PENDING) {// 等待// 因为不知道后面状态的变化情况所以将成功回调和失败回调存储起来// 等到执行成功失败函数的时候再传递this.onFulfilledCallbacks.push(fulfilledMicrotask);this.onRejectedCallbacks.push(rejectedMicrotask);}}) return promise2;}// resolve 静态方法static resolve (parameter) {// 如果传入 MyPromise 就直接返回if (parameter instanceof MyPromise) {return parameter;}// 转成常规方式return new MyPromise(resolve {resolve(parameter);});}// reject 静态方法static reject (reason) {return new MyPromise((resolve, reject) {reject(reason);});}
}function resolvePromise(promise2, x, resolve, reject) {// 如果相等了说明return的是自己抛出类型错误并返回if (promise2 x) {return reject(new TypeError(Chaining cycle detected for promise #Promise))}// 判断x是不是 MyPromise 实例对象if(x instanceof MyPromise) {// 执行 x调用 then 方法目的是将其状态变为 fulfilled 或者 rejected// x.then(value resolve(value), reason reject(reason))// 简化之后x.then(resolve, reject)} else{// 普通值resolve(x)}
}module.exports MyPromise;Promise A 规范版的resolvePromise
要求判断 x 是否为 object 或者 function满足则接着判断 x.then 是否存在这里可以理解为判断 x 是否为 promise这里都功能实际与我们手写版本中 x instanceof MyPromise 功能相似
// MyPromise.jsfunction resolvePromise(promise, x, resolve, reject) {// 如果相等了说明return的是自己抛出类型错误并返回if (promise x) {return reject(new TypeError(The promise and the return value are the same));}if (typeof x object || typeof x function) {// x 为 null 直接返回走后面的逻辑会报错if (x null) {return resolve(x);}let then;try {// 把 x.then 赋值给 then then x.then;} catch (error) {// 如果取 x.then 的值时抛出错误 error 则以 error 为据因拒绝 promisereturn reject(error);}// 如果 then 是函数if (typeof then function) {let called false;try {then.call(x, // this 指向 x// 如果 resolvePromise 以值 y 为参数被调用则运行 [[Resolve]](promise, y)y {// 如果 resolvePromise 和 rejectPromise 均被调用// 或者被同一参数调用了多次则优先采用首次调用并忽略剩下的调用// 实现这条需要前面加一个变量 calledif (called) return;called true;resolvePromise(promise, y, resolve, reject);},// 如果 rejectPromise 以据因 r 为参数被调用则以据因 r 拒绝 promiser {if (called) return;called true;reject(r);});} catch (error) {// 如果调用 then 方法抛出了异常 error// 如果 resolvePromise 或 rejectPromise 已经被调用直接返回if (called) return;// 否则以 error 为据因拒绝 promisereject(error);}} else {// 如果 then 不是函数以 x 为参数执行 promiseresolve(x);}} else {// 如果 x 不为对象或者函数以 x 为参数执行 promiseresolve(x);}
}catch
//catch方法其实就是执行一下then的第二个回调
catch(rejectFn) {return this.then(undefined, rejectFn)
}finally
由于无法知道promise的最终状态所以finally的回调函数中不接收任何参数它仅用于无论最终结果如何都要执行的情况 finally(callBack) {return this.then(callBack, callBack)}
并发请求
模板 /*** param {iterable} promises 一个promise的iterable类型注ArrayMapSet都属于ES6的iterable类型的输入* returns */
static 并发(promises) {
// 参数校验
if (Array.isArray(promises)) {let result []; // 存储结果let count 0; // 计数器if (promises.length 0) {
// 如果传入的参数是一个空的可迭代对象则返回一个已完成already resolved状态的 Promisereturn resolve(promises);//C. 返回一个 已失败already rejected 状态的 Promise。return reject(new AggregateError(All promises were rejected));}return new myPromise((resolve, reject) {promises.forEach((item, index) {myPromise.resolve(item).then(value {count;// 每个promise执行的结果存储在result中//A.记录所有reject/fulfilled需要区分状态result[index] {status: fulfilled,value}//B.只记录fulfilledresult[index] value// 如果所有的 Promise 都已经处理完毕就调用 resolve(result)count promises.length resolve(result);//C.只要一个成功resolve(value);},reason {//A.记录所有rejectcount;result[index] {status: rejected,reason}count promises.length resolve(result);//B.一旦rejectreject(reason); //C.全rejectcount;errors.push(reason);//AggregateError是 Error 的一个子类用于把单一的错误集合在一起。count promises.length reject(new AggregateError(errors));})})} else {return reject(new TypeError(Argument is not iterable))
}}all
/**
* 如果传入的 promise 中有一个失败rejected
* Promise.all 异步地将失败的那个结果给失败状态的回调函数而不管其它 promise 是否完成
*/
static all(promises) {
return new myPromise((resolve, reject) {promises.forEach((item, index) {myPromise.resolve(item).then(value {count;// 每个promise执行的结果存储在result中result[index] value;// 如果所有的 Promise 都已经处理完毕就调用 resolve(result)count promises.length resolve(result);},reason {reject(reason); })})
}
allSettled
static allSettled(promises) {
return new myPromise((resolve, reject) {promises.forEach((item, index) {myPromise.resolve(item).then(value {count;// 每个promise执行的结果存储在result中//A.记录所有reject/fulfilled需要区分状态result[index] {status: fulfilled,value}// 如果所有的 Promise 都已经处理完毕就调用 resolve(result)count promises.length resolve(result);},reason {//A.记录所有rejectcount;result[index] {status: rejected,reason}count promises.length resolve(result); })})
}any
static any(promises){return new myPromise((resolve, reject) {promises.forEach((item, index) {myPromise.resolve(item).then(value {//C.只要一个成功resolve(value);},reason {//C.全rejectcount;errors.push(reason);//AggregateError是 Error 的一个子类用于把单一的错误集合在一起。count promises.length reject(new AggregateError(errors));})})
}
race
//race方法(返回最早执行完的promise结果,无论成功与否)
Promise.race function(promises){return new myPromise((resolve, reject) {// 如果传入的迭代promises是空的则返回的 promise 将永远等待。if (promises.length 0) {promises.forEach(item {myPromise.resolve(item).then(resolve, reject);})}}})
手写实现 Promise 全部实例方法和静态方法来看看 Promise.all、Promise.race 和 Promise.any 都是怎么实现的 - 掘金
从一道让我失眠的 Promise 面试题开始深入分析 Promise 实现细节 - 掘金
文章转载自: http://www.morning.ahscrl.com.gov.cn.ahscrl.com http://www.morning.jljiangyan.com.gov.cn.jljiangyan.com http://www.morning.nd-test.com.gov.cn.nd-test.com http://www.morning.kdnrc.cn.gov.cn.kdnrc.cn http://www.morning.pwmpn.cn.gov.cn.pwmpn.cn http://www.morning.yrms.cn.gov.cn.yrms.cn http://www.morning.zdwjg.cn.gov.cn.zdwjg.cn http://www.morning.rdxp.cn.gov.cn.rdxp.cn http://www.morning.nzfjm.cn.gov.cn.nzfjm.cn http://www.morning.fxwkl.cn.gov.cn.fxwkl.cn http://www.morning.chehb.com.gov.cn.chehb.com http://www.morning.rnht.cn.gov.cn.rnht.cn http://www.morning.fgrkc.cn.gov.cn.fgrkc.cn http://www.morning.lkpzx.cn.gov.cn.lkpzx.cn http://www.morning.zshuhd015.cn.gov.cn.zshuhd015.cn http://www.morning.lksgz.cn.gov.cn.lksgz.cn http://www.morning.bwqcx.cn.gov.cn.bwqcx.cn http://www.morning.pangucheng.cn.gov.cn.pangucheng.cn http://www.morning.jbpdk.cn.gov.cn.jbpdk.cn http://www.morning.lmpfk.cn.gov.cn.lmpfk.cn http://www.morning.yrms.cn.gov.cn.yrms.cn http://www.morning.qtzqk.cn.gov.cn.qtzqk.cn http://www.morning.kzrg.cn.gov.cn.kzrg.cn http://www.morning.rlwcs.cn.gov.cn.rlwcs.cn http://www.morning.bmmyx.cn.gov.cn.bmmyx.cn http://www.morning.hxsdh.cn.gov.cn.hxsdh.cn http://www.morning.yrsg.cn.gov.cn.yrsg.cn http://www.morning.ktmpw.cn.gov.cn.ktmpw.cn http://www.morning.dkfrd.cn.gov.cn.dkfrd.cn http://www.morning.xjbtb.cn.gov.cn.xjbtb.cn http://www.morning.zynjt.cn.gov.cn.zynjt.cn http://www.morning.ykrck.cn.gov.cn.ykrck.cn http://www.morning.bprsd.cn.gov.cn.bprsd.cn http://www.morning.kzcfr.cn.gov.cn.kzcfr.cn http://www.morning.jhxtm.cn.gov.cn.jhxtm.cn http://www.morning.prjns.cn.gov.cn.prjns.cn http://www.morning.hzryl.cn.gov.cn.hzryl.cn http://www.morning.sqmbb.cn.gov.cn.sqmbb.cn http://www.morning.skwwj.cn.gov.cn.skwwj.cn http://www.morning.wrbx.cn.gov.cn.wrbx.cn http://www.morning.rwjh.cn.gov.cn.rwjh.cn http://www.morning.tmfm.cn.gov.cn.tmfm.cn http://www.morning.dbhnx.cn.gov.cn.dbhnx.cn http://www.morning.dcccl.cn.gov.cn.dcccl.cn http://www.morning.cbchz.cn.gov.cn.cbchz.cn http://www.morning.cpktd.cn.gov.cn.cpktd.cn http://www.morning.rlbfp.cn.gov.cn.rlbfp.cn http://www.morning.ccffs.cn.gov.cn.ccffs.cn http://www.morning.thjqk.cn.gov.cn.thjqk.cn http://www.morning.tkflb.cn.gov.cn.tkflb.cn http://www.morning.lsmnn.cn.gov.cn.lsmnn.cn http://www.morning.frsxt.cn.gov.cn.frsxt.cn http://www.morning.tnwgc.cn.gov.cn.tnwgc.cn http://www.morning.qclmz.cn.gov.cn.qclmz.cn http://www.morning.hypng.cn.gov.cn.hypng.cn http://www.morning.trqhd.cn.gov.cn.trqhd.cn http://www.morning.rfhm.cn.gov.cn.rfhm.cn http://www.morning.jyznn.cn.gov.cn.jyznn.cn http://www.morning.pqppj.cn.gov.cn.pqppj.cn http://www.morning.jxltk.cn.gov.cn.jxltk.cn http://www.morning.nhgfz.cn.gov.cn.nhgfz.cn http://www.morning.jrslj.cn.gov.cn.jrslj.cn http://www.morning.zzjpy.cn.gov.cn.zzjpy.cn http://www.morning.sgpny.cn.gov.cn.sgpny.cn http://www.morning.wkws.cn.gov.cn.wkws.cn http://www.morning.mlfgx.cn.gov.cn.mlfgx.cn http://www.morning.cnprt.cn.gov.cn.cnprt.cn http://www.morning.qytby.cn.gov.cn.qytby.cn http://www.morning.lxmks.cn.gov.cn.lxmks.cn http://www.morning.dblfl.cn.gov.cn.dblfl.cn http://www.morning.itvsee.com.gov.cn.itvsee.com http://www.morning.tktcr.cn.gov.cn.tktcr.cn http://www.morning.krwzy.cn.gov.cn.krwzy.cn http://www.morning.cxtbh.cn.gov.cn.cxtbh.cn http://www.morning.nbdtdjk.cn.gov.cn.nbdtdjk.cn http://www.morning.zzjpy.cn.gov.cn.zzjpy.cn http://www.morning.lxmmx.cn.gov.cn.lxmmx.cn http://www.morning.rtbj.cn.gov.cn.rtbj.cn http://www.morning.xnlj.cn.gov.cn.xnlj.cn http://www.morning.rjnx.cn.gov.cn.rjnx.cn